diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 26e589c092ed..f95551d679b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -322,7 +322,7 @@ jobs: RUST_CONFIGURE_ARGS: "--enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set rust.lto=thin" RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_13.4.1.app + SELECT_XCODE: /Applications/Xcode_14.3.1.app NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 @@ -335,7 +335,7 @@ jobs: RUST_CONFIGURE_ARGS: "--enable-sanitizers --enable-profiler --set rust.jemalloc" RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_13.4.1.app + SELECT_XCODE: /Applications/Xcode_14.3.1.app NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 @@ -347,6 +347,7 @@ jobs: RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 + SELECT_XCODE: /Applications/Xcode_14.3.1.app NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 @@ -358,6 +359,7 @@ jobs: RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 + SELECT_XCODE: /Applications/Xcode_14.3.1.app NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 @@ -367,7 +369,7 @@ jobs: SCRIPT: "./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin" RUST_CONFIGURE_ARGS: "--enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false --set rust.lto=thin" RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - SELECT_XCODE: /Applications/Xcode_13.4.1.app + SELECT_XCODE: /Applications/Xcode_14.3.1.app USE_XCODE_CLANG: 1 MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 @@ -381,7 +383,7 @@ jobs: SCRIPT: "./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin" RUST_CONFIGURE_ARGS: "--enable-sanitizers --enable-profiler --set rust.jemalloc" RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - SELECT_XCODE: /Applications/Xcode_13.4.1.app + SELECT_XCODE: /Applications/Xcode_14.3.1.app USE_XCODE_CLANG: 1 MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 diff --git a/Cargo.lock b/Cargo.lock index fff30b0f27bc..d552bb655b40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,7 +147,21 @@ dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", - "anstyle-wincon", + "anstyle-wincon 2.1.0", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstream" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon 3.0.2", "colorchoice", "utf8parse", ] @@ -186,6 +200,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.75" @@ -520,7 +544,7 @@ version = "4.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56" dependencies = [ - "anstream", + "anstream 0.5.0", "anstyle", "clap_lex", "strsim", @@ -558,7 +582,7 @@ checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" name = "clippy" version = "0.1.78" dependencies = [ - "anstream", + "anstream 0.5.0", "clippy_config", "clippy_lints", "clippy_utils", @@ -1234,6 +1258,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + [[package]] name = "env_logger" version = "0.10.0" @@ -1247,6 +1281,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e7cf40684ae96ade6232ed84582f40ce0a66efcd43a5117aef610534f8e0b8" +dependencies = [ + "anstream 0.6.11", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "equivalent" version = "1.0.0" @@ -1638,9 +1685,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.3.7" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c3372087601b532857d332f5957cbae686da52bb7810bf038c3e3c3cc2fa0d" +checksum = "ab283476b99e66691dee3f1640fea91487a8d81f50fb5ecc75538f8f8879a1e4" dependencies = [ "log", "pest", @@ -2227,6 +2274,7 @@ dependencies = [ name = "linkchecker" version = "0.1.0" dependencies = [ + "html5ever", "once_cell", "regex", ] @@ -2335,9 +2383,9 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.36" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80992cb0e05f22cc052c99f8e883f1593b891014b96a8b4637fd274d7030c85e" +checksum = "0c33564061c3c640bed5ace7d6a2a1b65f2c64257d1ac930c15e94ed0fb561d3" dependencies = [ "ammonia", "anyhow", @@ -2345,14 +2393,13 @@ dependencies = [ "clap", "clap_complete", "elasticlunr-rs", - "env_logger", + "env_logger 0.11.1", "handlebars", "log", "memchr", "once_cell", "opener", - "pathdiff", - "pulldown-cmark", + "pulldown-cmark 0.10.0", "regex", "serde", "serde_json", @@ -2471,7 +2518,7 @@ dependencies = [ "aes", "colored", "ctrlc", - "env_logger", + "env_logger 0.10.0", "getrandom", "jemalloc-sys", "lazy_static", @@ -2689,7 +2736,7 @@ dependencies = [ "camino", "clap", "derive_builder", - "env_logger", + "env_logger 0.10.0", "fs_extra", "glob", "humansize", @@ -3012,6 +3059,24 @@ dependencies = [ "unicase", ] +[[package]] +name = "pulldown-cmark" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce76ce678ffc8e5675b22aa1405de0b7037e2fdf8913fea40d1926c6fe1e6e7" +dependencies = [ + "bitflags 2.4.1", + "memchr", + "pulldown-cmark-escape", + "unicase", +] + +[[package]] +name = "pulldown-cmark-escape" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d8f9aa0e3cbcfaf8bf00300004ee3b72f74770f9cbac93f6928771f613276b" + [[package]] name = "punycode" version = "0.4.1" @@ -3271,7 +3336,7 @@ name = "rustbook" version = "0.1.0" dependencies = [ "clap", - "env_logger", + "env_logger 0.10.0", "mdbook", ] @@ -4427,7 +4492,7 @@ name = "rustc_resolve" version = "0.0.0" dependencies = [ "bitflags 2.4.1", - "pulldown-cmark", + "pulldown-cmark 0.9.6", "rustc_arena", "rustc_ast", "rustc_ast_pretty", @@ -4971,9 +5036,9 @@ checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" [[package]] name = "shlex" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "siphasher" diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index f87269960bc8..44e4dea34817 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -403,8 +403,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } let typeck = self.infcx.tcx.typeck(self.mir_def_id()); - let hir_id = hir.parent_id(expr.hir_id); - let parent = self.infcx.tcx.hir_node(hir_id); + let parent = self.infcx.tcx.parent_hir_node(expr.hir_id); let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind && let Some(def_id) = typeck.type_dependent_def_id(parent_expr.hir_id) @@ -1660,8 +1659,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Check that the parent of the closure is a method call, // with receiver matching with local's type (modulo refs) - let parent = hir.parent_id(closure_expr.hir_id); - if let hir::Node::Expr(parent) = tcx.hir_node(parent) { + if let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_expr.hir_id) { if let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind { let recv_ty = typeck_results.expr_ty(recv); diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 9e7fd45ec19a..5aed18ca4563 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -944,7 +944,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let hir = self.infcx.tcx.hir(); let closure_id = self.mir_hir_id(); let closure_span = self.infcx.tcx.def_span(self.mir_def_id()); - let fn_call_id = hir.parent_id(closure_id); + let fn_call_id = self.infcx.tcx.parent_hir_id(closure_id); let node = self.infcx.tcx.hir_node(fn_call_id); let def_id = hir.enclosing_body_owner(fn_call_id); let mut look_at_return = true; @@ -1034,7 +1034,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let InstanceDef::Item(def_id) = source.instance && let Some(Node::Expr(hir::Expr { hir_id, kind, .. })) = hir.get_if_local(def_id) && let ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Closure, .. }) = kind - && let Some(Node::Expr(expr)) = hir.find_parent(*hir_id) + && let Node::Expr(expr) = self.infcx.tcx.parent_hir_node(*hir_id) { let mut cur_expr = expr; while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind { diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index e69d2ca966ba..7529ec53a986 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -214,8 +214,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let Some(id) = placeholder.bound.kind.get_id() && let Some(placeholder_id) = id.as_local() && let gat_hir_id = self.infcx.tcx.local_def_id_to_hir_id(placeholder_id) - && let Some(generics_impl) = - hir.get_parent(hir.parent_id(gat_hir_id)).generics() + && let Some(generics_impl) = self + .infcx + .tcx + .parent_hir_node(self.infcx.tcx.parent_hir_id(gat_hir_id)) + .generics() { Some((gat_hir_id, generics_impl)) } else { diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 546001a25b29..85ebbb00c5f2 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -98,6 +98,8 @@ const_eval_error = {$error_kind -> const_eval_exact_div_has_remainder = exact_div: {$a} cannot be divided by {$b} without remainder +const_eval_extern_static = + cannot access extern static ({$did}) const_eval_fn_ptr_call = function pointers need an RFC before allowed to be called in {const_eval_const_context}s const_eval_for_loop_into_iter_non_const = @@ -172,6 +174,10 @@ const_eval_invalid_meta = invalid metadata in wide pointer: total size is bigger than largest supported object const_eval_invalid_meta_slice = invalid metadata in wide pointer: slice is bigger than largest supported object + +const_eval_invalid_niched_enum_variant_written = + trying to set discriminant of a {$ty} to the niched variant, but the value does not match + const_eval_invalid_str = this string is not valid UTF-8: {$err} const_eval_invalid_tag = @@ -298,8 +304,6 @@ const_eval_raw_ptr_to_int = .note = at compile-time, pointers do not have an integer value .note2 = avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior -const_eval_read_extern_static = - cannot read from extern static ({$did}) const_eval_read_pointer_as_int = unable to turn pointer into integer const_eval_realloc_or_alloc_with_offset = diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index fb89b49faded..a649526c1964 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -497,6 +497,9 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch, UninhabitedEnumVariantWritten(_) => const_eval_uninhabited_enum_variant_written, UninhabitedEnumVariantRead(_) => const_eval_uninhabited_enum_variant_read, + InvalidNichedEnumVariantWritten { .. } => { + const_eval_invalid_niched_enum_variant_written + } AbiMismatchArgument { .. } => const_eval_incompatible_types, AbiMismatchReturn { .. } => const_eval_incompatible_return_types, } @@ -585,6 +588,9 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { builder.arg("target_size", info.target_size); builder.arg("data_size", info.data_size); } + InvalidNichedEnumVariantWritten { enum_ty } => { + builder.arg("ty", enum_ty.to_string()); + } AbiMismatchArgument { caller_ty, callee_ty } | AbiMismatchReturn { caller_ty, callee_ty } => { builder.arg("caller_ty", caller_ty.to_string()); @@ -793,7 +799,7 @@ impl ReportErrorExt for UnsupportedOpInfo { UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_copy, UnsupportedOpInfo::ReadPointerAsInt(_) => const_eval_read_pointer_as_int, UnsupportedOpInfo::ThreadLocalStatic(_) => const_eval_thread_local_static, - UnsupportedOpInfo::ReadExternStatic(_) => const_eval_read_extern_static, + UnsupportedOpInfo::ExternStatic(_) => const_eval_extern_static, } } fn add_args(self, _: &DiagCtxt, builder: &mut DiagnosticBuilder<'_, G>) { @@ -812,7 +818,7 @@ impl ReportErrorExt for UnsupportedOpInfo { OverwritePartialPointer(ptr) | ReadPartialPointer(ptr) => { builder.arg("ptr", ptr); } - ThreadLocalStatic(did) | ReadExternStatic(did) => { + ThreadLocalStatic(did) | ExternStatic(did) => { builder.arg("did", format!("{did:?}")); } } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 0cb5c634b22b..6d470ff162e0 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -415,7 +415,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - fn unsize_into( + pub fn unsize_into( &mut self, src: &OpTy<'tcx, M::Provenance>, cast_ty: TyAndLayout<'tcx>, diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index bb8c17cf7791..e951a77611c2 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -85,6 +85,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Write result. let niche_dest = self.project_field(dest, tag_field)?; self.write_immediate(*tag_val, &niche_dest)?; + } else { + // The untagged variant is implicitly encoded simply by having a value that is + // outside the niche variants. But what if the data stored here does not + // actually encode this variant? That would be bad! So let's double-check... + let actual_variant = self.read_discriminant(&dest.to_op(self)?)?; + if actual_variant != variant_index { + throw_ub!(InvalidNichedEnumVariantWritten { enum_ty: dest.layout().ty }); + } } } } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 38ad8cbf3a69..4acf4ed893c5 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -557,7 +557,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if self.tcx.is_foreign_item(def_id) { // This is unreachable in Miri, but can happen in CTFE where we actually *do* support // referencing arbitrary (declared) extern statics. - throw_unsup!(ReadExternStatic(def_id)); + throw_unsup!(ExternStatic(def_id)); } // We don't give a span -- statics don't need that, they cannot be generic or associated. diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 3a441c1d649c..7b68a37fdf3e 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -149,6 +149,8 @@ where "`field` projection called on a slice -- call `index` projection instead" ); let offset = base.layout().fields.offset(field); + // Computing the layout does normalization, so we get a normalized type out of this + // even if the field type is non-normalized (possible e.g. via associated types). let field_layout = base.layout().field(self, field); // Offset may need adjustment for unsized fields. diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index de0590a4b14d..340a496a6899 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -153,6 +153,16 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { // We visited all parts of this one. return Ok(()); } + + // Non-normalized types should never show up here. + ty::Param(..) + | ty::Alias(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(..) + | ty::Error(..) => throw_inval!(TooGeneric), + + // The rest is handled below. _ => {} }; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs index 98276ff2e68d..1be2a2bc1f3c 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs @@ -131,11 +131,10 @@ fn is_parent_const_stable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { let local_def_id = def_id.expect_local(); let hir_id = tcx.local_def_id_to_hir_id(local_def_id); - let Some(parent) = tcx.hir().opt_parent_id(hir_id) else { return false }; - - if !tcx.is_const_trait_impl_raw(parent.owner.def_id.to_def_id()) { + let parent_owner_id = tcx.parent_hir_id(hir_id).owner; + if !tcx.is_const_trait_impl_raw(parent_owner_id.to_def_id()) { return false; } - tcx.lookup_const_stability(parent.owner).is_some_and(|stab| stab.is_const_stable()) + tcx.lookup_const_stability(parent_owner_id).is_some_and(|stab| stab.is_const_stable()) } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 03717a4d654c..2deb18484ec2 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -516,6 +516,17 @@ impl Diagnostic { /// Helper for pushing to `self.suggestions`, if available (not disable). fn push_suggestion(&mut self, suggestion: CodeSuggestion) { + for subst in &suggestion.substitutions { + for part in &subst.parts { + let span = part.span; + let call_site = span.ctxt().outer_expn_data().call_site; + if span.in_derive_expansion() && span.overlaps_or_adjacent(call_site) { + // Ignore if spans is from derive macro. + return; + } + } + } + if let Ok(suggestions) = &mut self.suggestions { suggestions.push(suggestion); } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index abf7a695fd2d..a7a1c69b9bed 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3508,7 +3508,7 @@ impl<'hir> Node<'hir> { /// ```ignore (illustrative) /// ctor /// .ctor_hir_id() - /// .and_then(|ctor_id| tcx.hir().find_parent(ctor_id)) + /// .map(|ctor_id| tcx.parent_hir_node(ctor_id)) /// .and_then(|parent| parent.ident()) /// ``` pub fn ident(&self) -> Option { diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index d438d3e7c60b..1ae3ebaebbbb 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2749,14 +2749,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { arg_idx: Option, ) -> Option> { let tcx = self.tcx(); - let hir = tcx.hir(); - let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), ident, .. }) = tcx.hir_node(fn_hir_id) else { return None; }; - let i = hir.get_parent(fn_hir_id).expect_item().expect_impl(); + let i = tcx.parent_hir_node(fn_hir_id).expect_item().expect_impl(); let trait_ref = self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty)); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 96b0cffc8a3d..dc391ab5648c 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -12,7 +12,7 @@ use rustc_errors::{codes::*, struct_span_code_err, DiagnosticMessage}; use rustc_hir as hir; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym}; use rustc_target::spec::abi::Abi; fn equate_intrinsic_type<'tcx>( @@ -133,7 +133,17 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir /// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`, /// and in `library/core/src/intrinsics.rs`. pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { - let param = |n| Ty::new_param(tcx, n, Symbol::intern(&format!("P{n}"))); + let generics = tcx.generics_of(it.owner_id); + let param = |n| { + if let Some(&ty::GenericParamDef { + name, kind: ty::GenericParamDefKind::Type { .. }, .. + }) = generics.opt_param_at(n as usize, tcx) + { + Ty::new_param(tcx, n, name) + } else { + Ty::new_error_with_message(tcx, tcx.def_span(it.owner_id), "expected param") + } + }; let intrinsic_id = it.owner_id.to_def_id(); let intrinsic_name = tcx.item_name(intrinsic_id); let name_str = intrinsic_name.as_str(); @@ -478,9 +488,16 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { /// Type-check `extern "platform-intrinsic" { ... }` functions. pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { + let generics = tcx.generics_of(it.owner_id); let param = |n| { - let name = Symbol::intern(&format!("P{n}")); - Ty::new_param(tcx, n, name) + if let Some(&ty::GenericParamDef { + name, kind: ty::GenericParamDefKind::Type { .. }, .. + }) = generics.opt_param_at(n as usize, tcx) + { + Ty::new_param(tcx, n, name) + } else { + Ty::new_error_with_message(tcx, tcx.def_span(it.owner_id), "expected param") + } }; let name = it.ident.name; diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 5a3878445937..fc911ecdad2f 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -25,9 +25,13 @@ use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{self, ObligationCause}; use std::collections::BTreeMap; -pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Result<(), ErrorGuaranteed> { +pub fn check_trait( + tcx: TyCtxt<'_>, + trait_def_id: DefId, + impl_def_id: LocalDefId, +) -> Result<(), ErrorGuaranteed> { let lang_items = tcx.lang_items(); - let checker = Checker { tcx, trait_def_id }; + let checker = Checker { tcx, trait_def_id, impl_def_id }; let mut res = checker.check(lang_items.drop_trait(), visit_implementation_of_drop); res = res.and(checker.check(lang_items.copy_trait(), visit_implementation_of_copy)); res = res.and( @@ -45,20 +49,16 @@ pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Result<(), ErrorGuar struct Checker<'tcx> { tcx: TyCtxt<'tcx>, trait_def_id: DefId, + impl_def_id: LocalDefId, } impl<'tcx> Checker<'tcx> { - fn check(&self, trait_def_id: Option, mut f: F) -> Result<(), ErrorGuaranteed> - where - F: FnMut(TyCtxt<'tcx>, LocalDefId) -> Result<(), ErrorGuaranteed>, - { - let mut res = Ok(()); - if Some(self.trait_def_id) == trait_def_id { - for &impl_def_id in self.tcx.hir().trait_impls(self.trait_def_id) { - res = res.and(f(self.tcx, impl_def_id)); - } - } - res + fn check( + &self, + trait_def_id: Option, + f: impl FnOnce(TyCtxt<'tcx>, LocalDefId) -> Result<(), ErrorGuaranteed>, + ) -> Result<(), ErrorGuaranteed> { + if Some(self.trait_def_id) == trait_def_id { f(self.tcx, self.impl_def_id) } else { Ok(()) } } } @@ -92,10 +92,10 @@ fn visit_implementation_of_copy( debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type); - let span = match tcx.hir().expect_item(impl_did).expect_impl() { - hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => return Ok(()), - hir::Impl { self_ty, .. } => self_ty.span, - }; + if let ty::ImplPolarity::Negative = tcx.impl_polarity(impl_did) { + return Ok(()); + } + let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; let cause = traits::ObligationCause::misc(span, impl_did); match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) { @@ -121,10 +121,10 @@ fn visit_implementation_of_const_param_ty( let param_env = tcx.param_env(impl_did); - let span = match tcx.hir().expect_item(impl_did).expect_impl() { - hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => return Ok(()), - impl_ => impl_.self_ty.span, - }; + if let ty::ImplPolarity::Negative = tcx.impl_polarity(impl_did) { + return Ok(()); + } + let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; let cause = traits::ObligationCause::misc(span, impl_did); match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) { diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 817dab993a37..7f59763f2a08 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -124,22 +124,25 @@ pub fn provide(providers: &mut Providers) { } fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed> { + // If there are no impls for the trait, then "all impls" are trivially coherent and we won't check anything + // anyway. Thus we bail out even before the specialization graph, avoiding the dep_graph edge. + let Some(impls) = tcx.all_local_trait_impls(()).get(&def_id) else { return Ok(()) }; // Trigger building the specialization graph for the trait. This will detect and report any // overlap errors. let mut res = tcx.ensure().specialization_graph_of(def_id); - let impls = tcx.hir().trait_impls(def_id); for &impl_def_id in impls { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(); res = res.and(check_impl(tcx, impl_def_id, trait_ref)); res = res.and(check_object_overlap(tcx, impl_def_id, trait_ref)); - res = res.and(unsafety::check_item(tcx, impl_def_id)); + res = res.and(unsafety::check_item(tcx, impl_def_id, trait_ref)); res = res.and(tcx.ensure().orphan_check_impl(impl_def_id)); + res = res.and(builtin::check_trait(tcx, def_id, impl_def_id)); } - res.and(builtin::check_trait(tcx, def_id)) + res } /// Checks whether an impl overlaps with the automatic `impl Trait for dyn Trait`. diff --git a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs index e3b5c724cdee..d217d53587d5 100644 --- a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs +++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs @@ -4,94 +4,91 @@ use rustc_errors::{codes::*, struct_span_code_err}; use rustc_hir as hir; use rustc_hir::Unsafety; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{TraitRef, TyCtxt}; use rustc_span::def_id::LocalDefId; use rustc_span::ErrorGuaranteed; -pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> { +pub(super) fn check_item( + tcx: TyCtxt<'_>, + def_id: LocalDefId, + trait_ref: TraitRef<'_>, +) -> Result<(), ErrorGuaranteed> { let item = tcx.hir().expect_item(def_id); let impl_ = item.expect_impl(); - - if let Some(trait_ref) = tcx.impl_trait_ref(item.owner_id) { - let trait_ref = trait_ref.instantiate_identity(); - let trait_def = tcx.trait_def(trait_ref.def_id); - let unsafe_attr = - impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle"); - match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) { - (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => { - return Err(struct_span_code_err!( - tcx.dcx(), - tcx.def_span(def_id), - E0199, - "implementing the trait `{}` is not unsafe", - trait_ref.print_trait_sugared() - ) - .with_span_suggestion_verbose( - item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)), - "remove `unsafe` from this trait implementation", - "", - rustc_errors::Applicability::MachineApplicable, - ) - .emit()); - } - - (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => { - return Err(struct_span_code_err!( - tcx.dcx(), - tcx.def_span(def_id), - E0200, - "the trait `{}` requires an `unsafe impl` declaration", - trait_ref.print_trait_sugared() - ) - .with_note(format!( - "the trait `{}` enforces invariants that the compiler can't check. \ - Review the trait documentation and make sure this implementation \ - upholds those invariants before adding the `unsafe` keyword", - trait_ref.print_trait_sugared() - )) - .with_span_suggestion_verbose( - item.span.shrink_to_lo(), - "add `unsafe` to this trait implementation", - "unsafe ", - rustc_errors::Applicability::MaybeIncorrect, - ) - .emit()); - } - - (Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => { - return Err(struct_span_code_err!( - tcx.dcx(), - tcx.def_span(def_id), - E0569, - "requires an `unsafe impl` declaration due to `#[{}]` attribute", - attr_name - ) - .with_note(format!( - "the trait `{}` enforces invariants that the compiler can't check. \ - Review the trait documentation and make sure this implementation \ - upholds those invariants before adding the `unsafe` keyword", - trait_ref.print_trait_sugared() - )) - .with_span_suggestion_verbose( - item.span.shrink_to_lo(), - "add `unsafe` to this trait implementation", - "unsafe ", - rustc_errors::Applicability::MaybeIncorrect, - ) - .emit()); - } - - (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => { - // Reported in AST validation - tcx.dcx().span_delayed_bug(item.span, "unsafe negative impl"); - } - (_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_)) - | (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive) - | (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive) - | (Unsafety::Normal, None, Unsafety::Normal, _) => { - // OK - } + let trait_def = tcx.trait_def(trait_ref.def_id); + let unsafe_attr = impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle"); + match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) { + (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => { + return Err(struct_span_code_err!( + tcx.dcx(), + tcx.def_span(def_id), + E0199, + "implementing the trait `{}` is not unsafe", + trait_ref.print_trait_sugared() + ) + .with_span_suggestion_verbose( + item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)), + "remove `unsafe` from this trait implementation", + "", + rustc_errors::Applicability::MachineApplicable, + ) + .emit()); } + + (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => { + return Err(struct_span_code_err!( + tcx.dcx(), + tcx.def_span(def_id), + E0200, + "the trait `{}` requires an `unsafe impl` declaration", + trait_ref.print_trait_sugared() + ) + .with_note(format!( + "the trait `{}` enforces invariants that the compiler can't check. \ + Review the trait documentation and make sure this implementation \ + upholds those invariants before adding the `unsafe` keyword", + trait_ref.print_trait_sugared() + )) + .with_span_suggestion_verbose( + item.span.shrink_to_lo(), + "add `unsafe` to this trait implementation", + "unsafe ", + rustc_errors::Applicability::MaybeIncorrect, + ) + .emit()); + } + + (Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => { + return Err(struct_span_code_err!( + tcx.dcx(), + tcx.def_span(def_id), + E0569, + "requires an `unsafe impl` declaration due to `#[{}]` attribute", + attr_name + ) + .with_note(format!( + "the trait `{}` enforces invariants that the compiler can't check. \ + Review the trait documentation and make sure this implementation \ + upholds those invariants before adding the `unsafe` keyword", + trait_ref.print_trait_sugared() + )) + .with_span_suggestion_verbose( + item.span.shrink_to_lo(), + "add `unsafe` to this trait implementation", + "unsafe ", + rustc_errors::Applicability::MaybeIncorrect, + ) + .emit()); + } + + (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => { + // Reported in AST validation + tcx.dcx().span_delayed_bug(item.span, "unsafe negative impl"); + Ok(()) + } + (_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_)) + | (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive) + | (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive) + | (Unsafety::Normal, None, Unsafety::Normal, _) => Ok(()), } - Ok(()) } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index fbcebb7c87c9..f458ff01c104 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -224,11 +224,8 @@ pub(crate) fn placeholder_type_error_diag<'tcx>( is_fn = true; // Check if parent is const or static - let parent_id = tcx.hir().parent_id(hir_ty.hir_id); - let parent_node = tcx.hir_node(parent_id); - is_const_or_static = matches!( - parent_node, + tcx.parent_hir_node(hir_ty.hir_id), Node::Item(&hir::Item { kind: hir::ItemKind::Const(..) | hir::ItemKind::Static(..), .. @@ -1085,7 +1082,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder { // Do not try to infer the return type for a impl method coming from a trait - if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) = tcx.hir().get_parent(hir_id) + if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) = tcx.parent_hir_node(hir_id) && i.of_trait.is_some() { icx.astconv().ty_of_fn( diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 4860555de205..e5e731bbe8c6 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -51,7 +51,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // of a const parameter type, e.g. `struct Foo` is not allowed. None } else if tcx.features().generic_const_exprs { - let parent_node = tcx.hir().get_parent(hir_id); + let parent_node = tcx.parent_hir_node(hir_id); if let Node::Variant(Variant { disr_expr: Some(constant), .. }) = parent_node && constant.hir_id == hir_id { @@ -113,7 +113,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { Some(parent_def_id.to_def_id()) } } else { - let parent_node = tcx.hir().get_parent(hir_id); + let parent_node = tcx.parent_hir_node(hir_id); match parent_node { // HACK(eddyb) this provides the correct generics for repeat // expressions' count (i.e. `N` in `[x; N]`), and explicit diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index ab9ed6ef98d9..05755f98f202 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -315,8 +315,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // We create bi-directional Outlives predicates between the original // and the duplicated parameter, to ensure that they do not get out of sync. if let Node::Item(&Item { kind: ItemKind::OpaqueTy(..), .. }) = node { - let opaque_ty_id = tcx.hir().parent_id(hir_id); - let opaque_ty_node = tcx.hir_node(opaque_ty_id); + let opaque_ty_node = tcx.parent_hir_node(hir_id); let Node::Ty(&Ty { kind: TyKind::OpaqueDef(_, lifetimes, _), .. }) = opaque_ty_node else { bug!("unexpected {opaque_ty_node:?}") }; diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 3849c0893f47..1aa9c6929f81 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -732,7 +732,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { let Some(def_id) = def_id.as_local() else { continue }; let hir_id = self.tcx.local_def_id_to_hir_id(def_id); // Ensure that the parent of the def is an item, not HRTB - let parent_id = self.tcx.hir().parent_id(hir_id); + let parent_id = self.tcx.parent_hir_id(hir_id); if !parent_id.is_owner() { struct_span_code_err!( self.tcx.dcx(), diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 20a7663f8644..c0128afe2bf6 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -30,7 +30,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { ); }; - let parent_node_id = tcx.hir().parent_id(hir_id); + let parent_node_id = tcx.parent_hir_id(hir_id); let parent_node = tcx.hir_node(parent_node_id); let (generics, arg_idx) = match parent_node { @@ -79,7 +79,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { } Node::TypeBinding(binding @ &TypeBinding { hir_id: binding_id, .. }) - if let Node::TraitRef(trait_ref) = tcx.hir_node(tcx.hir().parent_id(binding_id)) => + if let Node::TraitRef(trait_ref) = tcx.parent_hir_node(binding_id) => { let Some(trait_def_id) = trait_ref.trait_def_id() else { return Ty::new_error_with_message( diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index 3b1ee2975fd9..52ff8b2e45d7 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -135,7 +135,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { // Here we check if the reference to the generic type // is from the 'of_trait' field of the enclosing impl - let parent = self.tcx.hir().get_parent(self.path_segment.hir_id); + let parent = self.tcx.parent_hir_node(self.path_segment.hir_id); let parent_item = self.tcx.hir_node_by_def_id( self.tcx.hir().get_parent_item(self.path_segment.hir_id).def_id, ); @@ -770,9 +770,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { num = num_trait_generics_except_self, ); - if let Some(parent_node) = self.tcx.hir().opt_parent_id(self.path_segment.hir_id) - && let hir::Node::Expr(expr) = self.tcx.hir_node(parent_node) - { + if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(self.path_segment.hir_id) { match &expr.kind { hir::ExprKind::Path(qpath) => self .suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path( diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 7ea0469dedde..0311aa94cd48 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -287,7 +287,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { // If this `if` expr is the parent's function return expr, // the cause of the type coercion is the return type, point at it. (#25228) - let hir_id = self.tcx.hir().parent_id(self.tcx.hir().parent_id(then_expr.hir_id)); + let hir_id = self.tcx.parent_hir_id(self.tcx.parent_hir_id(then_expr.hir_id)); let ret_reason = self.maybe_get_coercion_reason(hir_id, if_span); let cause = self.cause(if_span, ObligationCauseCode::IfExpressionWithNoElse); let mut error = false; @@ -396,7 +396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let node = self.tcx.hir_node(hir_id); if let hir::Node::Block(block) = node { // check that the body's parent is an fn - let parent = self.tcx.hir().get_parent(self.tcx.hir().parent_id(block.hir_id)); + let parent = self.tcx.parent_hir_node(self.tcx.parent_hir_id(block.hir_id)); if let (Some(expr), hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })) = (&block.expr, parent) { diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 299ee092a80d..c4271c66e1c9 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -361,7 +361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_decl_span = if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), .. - }) = hir.get_parent(hir_id) + }) = self.tcx.parent_hir_node(hir_id) { fn_decl_span } else if let Some(( @@ -383,11 +383,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { // Actually need to unwrap one more layer of HIR to get to // the _real_ closure... - let async_closure = hir.parent_id(parent_hir_id); if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), .. - }) = self.tcx.hir_node(async_closure) + }) = self.tcx.parent_hir_node(parent_hir_id) { fn_decl_span } else { @@ -415,8 +414,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, callee_expr: &'tcx hir::Expr<'tcx>, ) -> bool { - let hir_id = self.tcx.hir().parent_id(call_expr.hir_id); - let parent_node = self.tcx.hir_node(hir_id); + let parent_node = self.tcx.parent_hir_node(call_expr.hir_id); if let ( hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Array(_), .. }), hir::ExprKind::Tup(exp), diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 2beabc0835db..549ad44d7e34 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -186,17 +186,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let b = self.shallow_resolve(b); debug!("Coerce.tys({:?} => {:?})", a, b); - // Just ignore error types. - if let Err(guar) = (a, b).error_reported() { - // Best-effort try to unify these types -- we're already on the error path, - // so this will have the side-effect of making sure we have no ambiguities - // due to `[type error]` and `_` not coercing together. - let _ = self.commit_if_ok(|_| { - self.at(&self.cause, self.param_env).eq(DefineOpaqueTypes::Yes, a, b) - }); - return success(vec![], Ty::new_error(self.fcx.tcx, guar), vec![]); - } - // Coercing from `!` to any type is allowed: if a.is_never() { return success(simple(Adjust::NeverToAny)(b), b, vec![]); @@ -1605,7 +1594,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { err.span_label(cause.span, "return type is not `()`"); } ObligationCauseCode::BlockTailExpression(blk_id, ..) => { - let parent_id = fcx.tcx.hir().parent_id(blk_id); + let parent_id = fcx.tcx.parent_hir_id(blk_id); err = self.report_return_mismatched_types( cause, expected, @@ -1796,7 +1785,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { ) -> DiagnosticBuilder<'a> { let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err); - let parent_id = fcx.tcx.hir().parent_id(id); + let parent_id = fcx.tcx.parent_hir_id(id); let parent = fcx.tcx.hir_node(parent_id); if let Some(expr) = expression && let hir::Node::Expr(hir::Expr { diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index d8974251de04..fdb2cb69ee7f 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -298,7 +298,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::Node::Pat(pat) = self.tcx.hir_node(local_hir_id) else { return false; }; - let (init_ty_hir_id, init) = match hir.get_parent(pat.hir_id) { + let (init_ty_hir_id, init) = match self.tcx.parent_hir_node(pat.hir_id) { hir::Node::Local(hir::Local { ty: Some(ty), init, .. }) => (ty.hir_id, *init), hir::Node::Local(hir::Local { init: Some(init), .. }) => (init.hir_id, Some(*init)), _ => return false, @@ -445,7 +445,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } - if let hir::Node::Expr(parent_expr) = hir.get_parent(binding.hir_id) + if let hir::Node::Expr(parent_expr) = self.tcx.parent_hir_node(binding.hir_id) && let hir::ExprKind::MethodCall(segment, rcvr, args, _) = parent_expr.kind && rcvr.hir_id == binding.hir_id { @@ -557,7 +557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(TypeError::Sorts(ExpectedFound { expected, .. })) = error else { return; }; - let mut parent_id = self.tcx.hir().parent_id(expr.hir_id); + let mut parent_id = self.tcx.parent_hir_id(expr.hir_id); let mut parent; 'outer: loop { // Climb the HIR tree to see if the current `Expr` is part of a `break;` statement. @@ -568,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { break; }; parent = p; - parent_id = self.tcx.hir().parent_id(parent_id); + parent_id = self.tcx.parent_hir_id(parent_id); let hir::ExprKind::Break(destination, _) = parent.kind else { continue; }; @@ -578,7 +578,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Climb the HIR tree to find the (desugared) `loop` this `break` corresponds to. let parent = match self.tcx.hir_node(parent_id) { hir::Node::Expr(&ref parent) => { - parent_id = self.tcx.hir().parent_id(parent.hir_id); + parent_id = self.tcx.parent_hir_id(parent.hir_id); parent } hir::Node::Stmt(hir::Stmt { @@ -586,11 +586,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind: hir::StmtKind::Semi(&ref parent) | hir::StmtKind::Expr(&ref parent), .. }) => { - parent_id = self.tcx.hir().parent_id(*hir_id); + parent_id = self.tcx.parent_hir_id(*hir_id); parent } hir::Node::Block(_) => { - parent_id = self.tcx.hir().parent_id(parent_id); + parent_id = self.tcx.parent_hir_id(parent_id); parent } _ => break, @@ -677,8 +677,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, error: Option>, ) { - let parent = self.tcx.hir().parent_id(expr.hir_id); - match (self.tcx.hir_node(parent), error) { + match (self.tcx.parent_hir_node(expr.hir_id), error) { (hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. }), _) if init.hir_id == expr.hir_id => { @@ -724,16 +723,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let hir::Node::Pat(pat) = self.tcx.hir_node(*hir_id) { primary_span = pat.span; secondary_span = pat.span; - match self.tcx.hir().find_parent(pat.hir_id) { - Some(hir::Node::Local(hir::Local { ty: Some(ty), .. })) => { + match self.tcx.parent_hir_node(pat.hir_id) { + hir::Node::Local(hir::Local { ty: Some(ty), .. }) => { primary_span = ty.span; post_message = " type"; } - Some(hir::Node::Local(hir::Local { init: Some(init), .. })) => { + hir::Node::Local(hir::Local { init: Some(init), .. }) => { primary_span = init.span; post_message = " value"; } - Some(hir::Node::Param(hir::Param { ty_span, .. })) => { + hir::Node::Param(hir::Param { ty_span, .. }) => { primary_span = *ty_span; post_message = " parameter type"; } @@ -787,12 +786,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, error: Option>, ) { - let parent = self.tcx.hir().parent_id(expr.hir_id); let Some(TypeError::Sorts(ExpectedFound { expected, .. })) = error else { return; }; let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(lhs, rhs, _), .. }) = - self.tcx.hir_node(parent) + self.tcx.parent_hir_node(expr.hir_id) else { return; }; @@ -1017,7 +1015,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )) = expr.kind { let bind = self.tcx.hir_node(*bind_hir_id); - let parent = self.tcx.hir_node(self.tcx.hir().parent_id(*bind_hir_id)); + let parent = self.tcx.parent_hir_node(*bind_hir_id); if let hir::Node::Pat(hir::Pat { kind: hir::PatKind::Binding(_, _hir_id, _, _), .. }) = bind @@ -1088,7 +1086,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, checked_ty: Ty<'tcx>, ) { - let Some(hir::Node::Expr(parent_expr)) = self.tcx.hir().find_parent(expr.hir_id) else { + let hir::Node::Expr(parent_expr) = self.tcx.parent_hir_node(expr.hir_id) else { return; }; enum CallableKind { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 292b85eb97f7..31c97aab7fb8 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1016,7 +1016,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { original_expr_id: HirId, then: impl FnOnce(&hir::Expr<'_>), ) { - let mut parent = self.tcx.hir().parent_id(original_expr_id); + let mut parent = self.tcx.parent_hir_id(original_expr_id); loop { let node = self.tcx.hir_node(parent); match node { @@ -1038,15 +1038,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), .. }) => { - // Check if our original expression is a child of the condition of a while loop - let expr_is_ancestor = std::iter::successors(Some(original_expr_id), |id| { - self.tcx.hir().opt_parent_id(*id) - }) - .take_while(|id| *id != parent) - .any(|id| id == expr.hir_id); - // if it is, then we have a situation like `while Some(0) = value.get(0) {`, + // Check if our original expression is a child of the condition of a while loop. + // If it is, then we have a situation like `while Some(0) = value.get(0) {`, // where `while let` was more likely intended. - if expr_is_ancestor { + if self.tcx.hir().parent_id_iter(original_expr_id).any(|id| id == expr.hir_id) { then(expr); } break; @@ -1056,7 +1051,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | hir::Node::TraitItem(_) | hir::Node::Crate(_) => break, _ => { - parent = self.tcx.hir().parent_id(parent); + parent = self.tcx.parent_hir_id(parent); } } } @@ -1199,9 +1194,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && !matches!(lhs.kind, hir::ExprKind::Lit(_)) { // Do not suggest `if let x = y` as `==` is way more likely to be the intention. - let hir = self.tcx.hir(); if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) = - hir.get_parent(hir.parent_id(expr.hir_id)) + self.tcx.parent_hir_node(expr.hir_id) { err.span_suggestion_verbose( expr.span.shrink_to_lo(), @@ -2645,7 +2639,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(field.span, "method, not a field"); let expr_is_call = if let hir::Node::Expr(hir::Expr { kind: ExprKind::Call(callee, _args), .. }) = - self.tcx.hir().get_parent(expr.hir_id) + self.tcx.parent_hir_node(expr.hir_id) { expr.hir_id == callee.hir_id } else { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 36c3eef82fcc..7eb421ca8f5a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -764,7 +764,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let code = match lang_item { hir::LangItem::IntoFutureIntoFuture => { - if let hir::Node::Expr(into_future_call) = self.tcx.hir().get_parent(hir_id) + if let hir::Node::Expr(into_future_call) = self.tcx.parent_hir_node(hir_id) && let hir::ExprKind::Call(_, [arg0]) = &into_future_call.kind { Some(ObligationCauseCode::AwaitableExpr(arg0.hir_id)) @@ -956,12 +956,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .. }) => Some((hir::HirId::make_owner(owner_id.def_id), &sig.decl, ident, false)), Node::Expr(&hir::Expr { hir_id, kind: hir::ExprKind::Closure(..), .. }) - if let Some(Node::Item(&hir::Item { + if let Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), owner_id, .. - })) = self.tcx.hir().find_parent(hir_id) => + }) = self.tcx.parent_hir_node(hir_id) => { Some(( hir::HirId::make_owner(owner_id.def_id), @@ -1574,7 +1574,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool { let mut contained_in_place = false; - while let hir::Node::Expr(parent_expr) = self.tcx.hir().get_parent(expr_id) { + while let hir::Node::Expr(parent_expr) = self.tcx.parent_hir_node(expr_id) { match &parent_expr.kind { hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => { if lhs.hir_id == expr_id { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index 1ad79cb78c4b..6aa986b0df4c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -93,7 +93,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.find_ambiguous_parameter_in(def_id, error.root_obligation.predicate); } - let hir = self.tcx.hir(); let (expr, qpath) = match self.tcx.hir_node(hir_id) { hir::Node::Expr(expr) => { if self.closure_span_overlaps_error(error, expr.span) { @@ -122,7 +121,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir_id: call_hir_id, span: call_span, .. - }) = hir.get_parent(hir_id) + }) = self.tcx.parent_hir_node(hir_id) && callee.hir_id == hir_id { if self.closure_span_overlaps_error(error, *call_span) { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index ed0bdb9bdaa0..193c9a4b9087 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -681,9 +681,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check if the parent expression is a call to Pin::new. If it // is and we were expecting a Box, ergo Pin>, we // can suggest Box::pin. - let parent = self.tcx.hir().parent_id(expr.hir_id); let Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. }) = - self.tcx.hir_node(parent) + self.tcx.parent_hir_node(expr.hir_id) else { return false; }; @@ -1687,7 +1686,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return expr; }; - match self.tcx.hir_node(self.tcx.hir().parent_id(*hir_id)) { + match self.tcx.parent_hir_node(*hir_id) { // foo.clone() hir::Node::Local(hir::Local { init: Some(init), .. }) => { self.note_type_is_not_clone_inner_expr(init) @@ -1699,7 +1698,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .. }) => { let hir::Node::Local(hir::Local { init: Some(init), .. }) = - self.tcx.hir_node(self.tcx.hir().parent_id(*pat_hir_id)) + self.tcx.parent_hir_node(*pat_hir_id) else { return expr; }; @@ -1733,7 +1732,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr_path && let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding) && let hir::Node::Local(hir::Local { init: Some(init), .. }) = - self.tcx.hir_node(self.tcx.hir().parent_id(*hir_id)) + self.tcx.parent_hir_node(*hir_id) && let Expr { kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }), .. @@ -1899,8 +1898,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> bool { let map = self.tcx.hir(); let returned = matches!( - map.find_parent(expr.hir_id), - Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. })) + self.tcx.parent_hir_node(expr.hir_id), + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }) ) || map.get_return_block(expr.hir_id).is_some(); if returned && let ty::Adt(e, args_e) = expected.kind() @@ -1972,7 +1971,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Unroll desugaring, to make sure this works for `for` loops etc. loop { - parent = self.tcx.hir().parent_id(id); + parent = self.tcx.parent_hir_id(id); let parent_span = self.tcx.hir().span(parent); if parent_span.find_ancestor_inside(expr.span).is_some() { // The parent node is part of the same span, so is the result of the @@ -2211,24 +2210,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - let local_parent = self.tcx.hir().parent_id(local_id); - let Node::Param(hir::Param { hir_id: param_hir_id, .. }) = self.tcx.hir_node(local_parent) + let Node::Param(hir::Param { hir_id: param_hir_id, .. }) = + self.tcx.parent_hir_node(local_id) else { return None; }; - let param_parent = self.tcx.hir().parent_id(*param_hir_id); let Node::Expr(hir::Expr { hir_id: expr_hir_id, kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }), .. - }) = self.tcx.hir_node(param_parent) + }) = self.tcx.parent_hir_node(*param_hir_id) else { return None; }; - let expr_parent = self.tcx.hir().parent_id(*expr_hir_id); - let hir = self.tcx.hir_node(expr_parent); + let hir = self.tcx.parent_hir_node(*expr_hir_id); let closure_params_len = closure_fn_decl.inputs.len(); let ( Node::Expr(hir::Expr { @@ -2409,10 +2406,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => String::new(), }; - if let Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Assign(..), - .. - })) = self.tcx.hir().find_parent(expr.hir_id) + if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(..), .. }) = + self.tcx.parent_hir_node(expr.hir_id) { if mutability.is_mut() { // Suppressing this diagnostic, we'll properly print it in `check_expr_assign` @@ -2443,10 +2438,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // Suggest dereferencing the lhs for expressions such as `&T <= T` - if let Some(hir::Node::Expr(hir::Expr { + if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(_, lhs, ..), .. - })) = self.tcx.hir().find_parent(expr.hir_id) + }) = self.tcx.parent_hir_node(expr.hir_id) && let &ty::Ref(..) = self.check_expr(lhs).kind() { let (sugg, verbose) = make_sugg(lhs, lhs.span, "*"); @@ -2602,7 +2597,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || (checked_ty.is_box() && steps == 1) // We can always deref a binop that takes its arguments by ref. || matches!( - self.tcx.hir().get_parent(expr.hir_id), + self.tcx.parent_hir_node(expr.hir_id), hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, ..), .. }) if !op.node.is_by_value() ) @@ -2664,10 +2659,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Returns whether the given expression is an `else if`. fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool { if let hir::ExprKind::If(..) = expr.kind { - let parent_id = self.tcx.hir().parent_id(expr.hir_id); if let Node::Expr(hir::Expr { kind: hir::ExprKind::If(_, _, Some(else_expr)), .. - }) = self.tcx.hir_node(parent_id) + }) = self.tcx.parent_hir_node(expr.hir_id) { return else_expr.hir_id == expr.hir_id; } @@ -2704,7 +2698,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut sugg = vec![]; - if let Some(hir::Node::ExprField(field)) = self.tcx.hir().find_parent(expr.hir_id) { + if let hir::Node::ExprField(field) = self.tcx.parent_hir_node(expr.hir_id) { // `expr` is a literal field for a struct, only suggest if appropriate if field.is_shorthand { // This is a field literal @@ -3056,8 +3050,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { else { return; }; - let parent = self.tcx.hir().parent_id(expr.hir_id); - if let hir::Node::ExprField(_) = self.tcx.hir_node(parent) { + if let hir::Node::ExprField(_) = self.tcx.parent_hir_node(expr.hir_id) { // Ignore `Foo { field: a..Default::default() }` return; } @@ -3139,8 +3132,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else { return; }; - let Some(hir::Node::Local(hir::Local { ty: None, init: Some(init), .. })) = - self.tcx.hir().find_parent(pat.hir_id) + let hir::Node::Local(hir::Local { ty: None, init: Some(init), .. }) = + self.tcx.parent_hir_node(pat.hir_id) else { return; }; diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 06a146135670..c1af4b5983eb 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -220,7 +220,7 @@ fn typeck_with_fallback<'tcx>( span, })) } else if let Node::AnonConst(_) = node { - match tcx.hir_node(tcx.hir().parent_id(id)) { + match tcx.parent_hir_node(id) { Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(ref anon_const), .. }) if anon_const.hir_id == id => { diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 81b823935154..7fc51e36a2b6 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -128,7 +128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let sugg_span = if let SelfSource::MethodCall(expr) = source { // Given `foo.bar(baz)`, `expr` is `bar`, but we want to point to the whole thing. - self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)).span + self.tcx.hir().expect_expr(self.tcx.parent_hir_id(expr.hir_id)).span } else { span }; @@ -231,9 +231,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = kind && let hir::def::Res::Local(hir_id) = path.res && let hir::Node::Pat(b) = self.tcx.hir_node(hir_id) - && let Some(hir::Node::Param(p)) = self.tcx.hir().find_parent(b.hir_id) - && let Some(node) = self.tcx.hir().find_parent(p.hir_id) - && let Some(decl) = node.fn_decl() + && let hir::Node::Param(p) = self.tcx.parent_hir_node(b.hir_id) + && let Some(decl) = self.tcx.parent_hir_node(p.hir_id).fn_decl() && let Some(ty) = decl.inputs.iter().find(|ty| ty.span == p.ty_span) && let hir::TyKind::Ref(_, mut_ty) = &ty.kind && let hir::Mutability::Not = mut_ty.mutbl @@ -471,7 +470,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let SelfSource::MethodCall(rcvr_expr) = source { self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| { let call_expr = - self.tcx.hir().expect_expr(self.tcx.hir().parent_id(rcvr_expr.hir_id)); + self.tcx.hir().expect_expr(self.tcx.parent_hir_id(rcvr_expr.hir_id)); let probe = self.lookup_probe_for_diagnostic( item_name, output_ty, @@ -1020,7 +1019,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty, &item_segment, span, - tcx.hir().get_parent(rcvr_expr.hir_id).expect_expr(), + tcx.parent_hir_node(rcvr_expr.hir_id).expect_expr(), rcvr_expr, ) { err.span_note( @@ -1254,7 +1253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let msg = "remove this method call"; let mut fallback_span = true; if let SelfSource::MethodCall(expr) = source { - let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)); + let call_expr = self.tcx.hir().expect_expr(self.tcx.parent_hir_id(expr.hir_id)); if let Some(span) = call_expr.span.trim_start(expr.span) { err.span_suggestion(span, msg, "", Applicability::MachineApplicable); fallback_span = false; @@ -1753,7 +1752,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } else { - let call_expr = tcx.hir().expect_expr(tcx.hir().parent_id(expr.hir_id)); + let call_expr = tcx.hir().expect_expr(tcx.parent_hir_id(expr.hir_id)); if let Some(span) = call_expr.span.trim_start(item_name.span) { err.span_suggestion( @@ -1937,7 +1936,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span = tcx.hir().span(hir_id); let filename = tcx.sess.source_map().span_to_filename(span); - let parent_node = self.tcx.hir().get_parent(hir_id); + let parent_node = self.tcx.parent_hir_node(hir_id); let msg = format!( "you must specify a type for this binding, like `{concrete_type}`", ); @@ -2016,8 +2015,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name }; visitor.visit_body(body); - let parent = self.tcx.hir().parent_id(seg1.hir_id); - if let Node::Expr(call_expr) = self.tcx.hir_node(parent) + if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id) && let Some(expr) = visitor.result && let Some(self_ty) = self.node_ty_opt(expr.hir_id) { @@ -2056,7 +2054,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (fields, args) in self.get_field_candidates_considering_privacy(span, actual, mod_id, expr.hir_id) { - let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)); + let call_expr = self.tcx.hir().expect_expr(self.tcx.parent_hir_id(expr.hir_id)); let lang_items = self.tcx.lang_items(); let never_mention_traits = [ @@ -2133,7 +2131,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let SelfSource::MethodCall(expr) = source else { return; }; - let call_expr = tcx.hir().expect_expr(tcx.hir().parent_id(expr.hir_id)); + let call_expr = tcx.hir().expect_expr(tcx.parent_hir_id(expr.hir_id)); let ty::Adt(kind, args) = actual.kind() else { return; @@ -3250,8 +3248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; } - let parent = self.tcx.hir().parent_id(expr.hir_id); - if let Node::Expr(call_expr) = self.tcx.hir_node(parent) + if let Node::Expr(call_expr) = self.tcx.parent_hir_node(expr.hir_id) && let hir::ExprKind::MethodCall( hir::PathSegment { ident: method_name, .. }, self_expr, diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 9ce618985619..9a8444e6a2bc 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -383,7 +383,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; if self.check_for_missing_semi(expr, &mut err) - && let hir::Node::Expr(expr) = self.tcx.hir().get_parent(expr.hir_id) + && let hir::Node::Expr(expr) = self.tcx.parent_hir_node(expr.hir_id) && let hir::ExprKind::Assign(..) = expr.kind { // We defer to the later error produced by `check_lhs_assignable`. diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index fcf4b59e93fc..67aa92185852 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -720,8 +720,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let PatKind::Ref(inner, mutbl) = pat.kind && let PatKind::Binding(_, _, binding, ..) = inner.kind { - let binding_parent_id = tcx.hir().parent_id(pat.hir_id); - let binding_parent = tcx.hir_node(binding_parent_id); + let binding_parent = tcx.parent_hir_node(pat.hir_id); debug!(?inner, ?pat, ?binding_parent); let mutability = match mutbl { @@ -989,7 +988,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { res.descr(), ), ); - match self.tcx.hir().get_parent(pat.hir_id) { + match self.tcx.parent_hir_node(pat.hir_id) { hir::Node::PatField(..) => { e.span_suggestion_verbose( ident.span.shrink_to_hi(), diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index dd9ed80ca724..4d2d19b51e22 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -862,8 +862,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) { err.subdiagnostic(subdiag); } - if let Some(hir::Node::Expr(m)) = self.tcx.hir().find_parent(scrut_hir_id) - && let Some(hir::Node::Stmt(stmt)) = self.tcx.hir().find_parent(m.hir_id) + if let hir::Node::Expr(m) = self.tcx.parent_hir_node(scrut_hir_id) + && let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(m.hir_id) && let hir::StmtKind::Expr(_) = stmt.kind { err.span_suggestion_verbose( diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index f884ca83073d..3c42f13141dd 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -106,7 +106,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } p_def_id.as_local().and_then(|id| { let local_id = tcx.local_def_id_to_hir_id(id); - let generics = tcx.hir().find_parent(local_id)?.generics()?; + let generics = tcx.parent_hir_node(local_id).generics()?; Some((id, generics)) }) }); diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index bbe07b8ed72b..248e1c0fcc87 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -735,30 +735,29 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; local.pat.walk(&mut find_compatible_candidates); } - match hir.find_parent(blk.hir_id) { - Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => match hir.find_parent(*hir_id) { - Some(hir::Node::Arm(hir::Arm { pat, .. })) => { + match self.tcx.parent_hir_node(blk.hir_id) { + hir::Node::Expr(hir::Expr { hir_id, .. }) => match self.tcx.parent_hir_node(*hir_id) { + hir::Node::Arm(hir::Arm { pat, .. }) => { pat.walk(&mut find_compatible_candidates); } - Some( - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. }) - | hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(_, body), .. - }) - | hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)), - .. - }) - | hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(hir::Closure { body, .. }), - .. - }), - ) => { + + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. }) + | hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(_, body), .. + }) + | hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)), + .. + }) + | hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { body, .. }), + .. + }) => { for param in hir.body(*body).params { param.pat.walk(&mut find_compatible_candidates); } } - Some(hir::Node::Expr(hir::Expr { + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If( hir::Expr { kind: hir::ExprKind::Let(let_), .. }, @@ -766,7 +765,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { _, ), .. - })) if then_block.hir_id == *hir_id => { + }) if then_block.hir_id == *hir_id => { let_.pat.walk(&mut find_compatible_candidates); } _ => {} diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs index 7401654aea8b..0253f5a2df2a 100644 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs @@ -65,10 +65,10 @@ pub fn report_object_safety_error<'tcx>( && let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind { let mut hir_id = hir_id; - while let hir::Node::Ty(ty) = tcx.hir().get_parent(hir_id) { + while let hir::Node::Ty(ty) = tcx.parent_hir_node(hir_id) { hir_id = ty.hir_id; } - if tcx.hir().get_parent(hir_id).fn_sig().is_some() { + if tcx.parent_hir_node(hir_id).fn_sig().is_some() { // Do not suggest `impl Trait` when dealing with things like super-traits. err.span_suggestion_verbose( ty.span.until(trait_ref.span), diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index f10d3d4a68a7..6ee1d1ca9247 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1416,8 +1416,7 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub { } fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - let map = cx.tcx.hir(); - if matches!(map.get_parent(field.hir_id), Node::Variant(_)) { + if matches!(cx.tcx.parent_hir_node(field.hir_id), Node::Variant(_)) { return; } self.perform_lint(cx, "field", field.def_id, field.vis_span, false); diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 1c480ec8f53b..30f05444d210 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -933,7 +933,7 @@ impl<'tcx> LateContext<'tcx> { while let hir::ExprKind::Path(ref qpath) = expr.kind && let Some(parent_node) = match self.qpath_res(qpath, expr.hir_id) { - Res::Local(hir_id) => self.tcx.hir().find_parent(hir_id), + Res::Local(hir_id) => Some(self.tcx.parent_hir_node(hir_id)), _ => None, } && let Some(init) = match parent_node { @@ -977,7 +977,7 @@ impl<'tcx> LateContext<'tcx> { while let hir::ExprKind::Path(ref qpath) = expr.kind && let Some(parent_node) = match self.qpath_res(qpath, expr.hir_id) { - Res::Local(hir_id) => self.tcx.hir().find_parent(hir_id), + Res::Local(hir_id) => Some(self.tcx.parent_hir_node(hir_id)), Res::Def(_, def_id) => self.tcx.hir().get_if_local(def_id), _ => None, } diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs index 9a31aa062f01..78ac7f9b2354 100644 --- a/compiler/rustc_lint/src/drop_forget_useless.rs +++ b/compiler/rustc_lint/src/drop_forget_useless.rs @@ -214,8 +214,7 @@ fn is_single_call_in_arm<'tcx>( drop_expr: &'tcx Expr<'_>, ) -> bool { if arg.can_have_side_effects() { - let parent_node = cx.tcx.hir().find_parent(drop_expr.hir_id); - if let Some(Node::Arm(Arm { body, .. })) = &parent_node { + if let Node::Arm(Arm { body, .. }) = cx.tcx.parent_hir_node(drop_expr.hir_id) { return body.hir_id == drop_expr.hir_id; } } diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 516df14c8943..596221a8455c 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -144,15 +144,14 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { match &ty.kind { TyKind::Path(QPath::Resolved(_, path)) => { if lint_ty_kind_usage(cx, &path.res) { - let hir = cx.tcx.hir(); - let span = match hir.find_parent(ty.hir_id) { - Some(Node::Pat(Pat { + let span = match cx.tcx.parent_hir_node(ty.hir_id) { + Node::Pat(Pat { kind: PatKind::Path(qpath) | PatKind::TupleStruct(qpath, ..) | PatKind::Struct(qpath, ..), .. - })) => { + }) => { if let QPath::TypeRelative(qpath_ty, ..) = qpath && qpath_ty.hir_id == ty.hir_id { @@ -161,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { None } } - Some(Node::Expr(Expr { kind: ExprKind::Path(qpath), .. })) => { + Node::Expr(Expr { kind: ExprKind::Path(qpath), .. }) => { if let QPath::TypeRelative(qpath_ty, ..) = qpath && qpath_ty.hir_id == ty.hir_id { @@ -172,7 +171,7 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { } // Can't unify these two branches because qpath below is `&&` and above is `&` // and `A | B` paths don't play well together with adjustments, apparently. - Some(Node::Expr(Expr { kind: ExprKind::Struct(qpath, ..), .. })) => { + Node::Expr(Expr { kind: ExprKind::Struct(qpath, ..), .. }) => { if let QPath::TypeRelative(qpath_ty, ..) = qpath && qpath_ty.hir_id == ty.hir_id { diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 05b39829a12f..4ecd87e37d3b 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -427,7 +427,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { fn check_pat(&mut self, cx: &LateContext<'_>, p: &hir::Pat<'_>) { if let PatKind::Binding(_, hid, ident, _) = p.kind { - if let hir::Node::PatField(field) = cx.tcx.hir().get_parent(hid) { + if let hir::Node::PatField(field) = cx.tcx.parent_hir_node(hid) { if !field.is_shorthand { // Only check if a new name has been introduced, to avoid warning // on both the struct definition and this pattern. diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 1205395b8908..e4ebae2a9732 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -200,8 +200,7 @@ fn lint_overflowing_range_endpoint<'tcx>( ty: &str, ) -> bool { // Look past casts to support cases like `0..256 as u8` - let (expr, lit_span) = if let Node::Expr(par_expr) = - cx.tcx.hir_node(cx.tcx.hir().parent_id(expr.hir_id)) + let (expr, lit_span) = if let Node::Expr(par_expr) = cx.tcx.parent_hir_node(expr.hir_id) && let ExprKind::Cast(_, _) = par_expr.kind { (par_expr, expr.span) @@ -211,9 +210,8 @@ fn lint_overflowing_range_endpoint<'tcx>( // We only want to handle exclusive (`..`) ranges, // which are represented as `ExprKind::Struct`. - let par_id = cx.tcx.hir().parent_id(expr.hir_id); - let Node::ExprField(field) = cx.tcx.hir_node(par_id) else { return false }; - let Node::Expr(struct_expr) = cx.tcx.hir().get_parent(field.hir_id) else { return false }; + let Node::ExprField(field) = cx.tcx.parent_hir_node(expr.hir_id) else { return false }; + let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { return false }; if !is_range_literal(struct_expr) { return false; }; @@ -496,8 +494,7 @@ fn lint_uint_literal<'tcx>( _ => bug!(), }; if lit_val < min || lit_val > max { - let parent_id = cx.tcx.hir().parent_id(e.hir_id); - if let Node::Expr(par_e) = cx.tcx.hir_node(parent_id) { + if let Node::Expr(par_e) = cx.tcx.parent_hir_node(e.hir_id) { match par_e.kind { hir::ExprKind::Cast(..) => { if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 7cd2f58779f8..988388edfd5f 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -250,6 +250,7 @@ provide! { tcx, def_id, other, cdata, asyncness => { table_direct } fn_arg_names => { table } coroutine_kind => { table_direct } + coroutine_for_closure => { table } trait_def => { table } deduced_param_attrs => { table } is_type_alias_impl_trait => { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 79aa9a547f7c..6ca1973396f8 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1447,6 +1447,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { { self.tables.coroutine_kind.set(def_id.index, Some(coroutine_kind)) } + if def_kind == DefKind::Closure + && tcx.type_of(def_id).skip_binder().is_coroutine_closure() + { + self.tables + .coroutine_for_closure + .set_some(def_id.index, self.tcx.coroutine_for_closure(def_id).into()); + } if let DefKind::Enum | DefKind::Struct | DefKind::Union = def_kind { self.encode_info_for_adt(local_id); } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 2f7758826934..8205e995c196 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -443,6 +443,7 @@ define_tables! { asyncness: Table, fn_arg_names: Table>, coroutine_kind: Table, + coroutine_for_closure: Table, trait_def: Table>, trait_item_def_id: Table, expn_that_defined: Table>, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index bf72aac10332..50817dd0a809 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -82,7 +82,7 @@ impl<'hir> Iterator for ParentHirIterator<'hir> { } // There are nodes that do not have entries, so we need to skip them. - let parent_id = self.map.parent_id(self.current_id); + let parent_id = self.map.tcx.parent_hir_id(self.current_id); if parent_id == self.current_id { self.current_id = CRATE_HIR_ID; @@ -175,6 +175,28 @@ impl<'tcx> TyCtxt<'tcx> { self.opt_hir_node_by_def_id(id) .unwrap_or_else(|| bug!("couldn't find HIR node for def id {id:?}")) } + + /// Returns `HirId` of the parent HIR node of node with this `hir_id`. + /// Returns the same `hir_id` if and only if `hir_id == CRATE_HIR_ID`. + /// + /// If calling repeatedly and iterating over parents, prefer [`Map::parent_iter`]. + pub fn parent_hir_id(self, hir_id: HirId) -> HirId { + let HirId { owner, local_id } = hir_id; + if local_id == ItemLocalId::from_u32(0) { + self.hir_owner_parent(owner) + } else { + let parent_local_id = self.hir_owner_nodes(owner).nodes[local_id].parent; + // HIR indexing should have checked that. + debug_assert_ne!(parent_local_id, local_id); + HirId { owner, local_id: parent_local_id } + } + } + + /// Returns parent HIR node of node with this `hir_id`. + /// Returns HIR node of the same `hir_id` if and only if `hir_id == CRATE_HIR_ID`. + pub fn parent_hir_node(self, hir_id: HirId) -> Node<'tcx> { + self.hir_node(self.parent_hir_id(hir_id)) + } } impl<'hir> Map<'hir> { @@ -217,39 +239,6 @@ impl<'hir> Map<'hir> { self.tcx.definitions_untracked().def_path_hash(def_id) } - /// Finds the id of the parent node to this one. - /// - /// If calling repeatedly and iterating over parents, prefer [`Map::parent_iter`]. - pub fn opt_parent_id(self, id: HirId) -> Option { - if id.local_id == ItemLocalId::from_u32(0) { - // FIXME: This function never returns `None` right now, and the parent chain end is - // determined by checking for `parent(id) == id`. This function should return `None` - // for the crate root instead. - Some(self.tcx.hir_owner_parent(id.owner)) - } else { - let owner = self.tcx.hir_owner_nodes(id.owner); - let node = &owner.nodes[id.local_id]; - let hir_id = HirId { owner: id.owner, local_id: node.parent }; - // HIR indexing should have checked that. - debug_assert_ne!(id.local_id, node.parent); - Some(hir_id) - } - } - - #[track_caller] - pub fn parent_id(self, hir_id: HirId) -> HirId { - self.opt_parent_id(hir_id) - .unwrap_or_else(|| bug!("No parent for node {}", self.node_to_string(hir_id))) - } - - pub fn get_parent(self, hir_id: HirId) -> Node<'hir> { - self.tcx.hir_node(self.parent_id(hir_id)) - } - - pub fn find_parent(self, hir_id: HirId) -> Option> { - Some(self.tcx.hir_node(self.opt_parent_id(hir_id)?)) - } - pub fn get_if_local(self, id: DefId) -> Option> { id.as_local() .and_then(|id| Some(self.tcx.hir_node(self.tcx.opt_local_def_id_to_hir_id(id)?))) @@ -304,14 +293,13 @@ impl<'hir> Map<'hir> { /// which this is the body of, i.e., a `fn`, `const` or `static` /// item (possibly associated), a closure, or a `hir::AnonConst`. pub fn body_owner(self, BodyId { hir_id }: BodyId) -> HirId { - let parent = self.parent_id(hir_id); + let parent = self.tcx.parent_hir_id(hir_id); assert!(is_body_owner(self.tcx.hir_node(parent), hir_id), "{hir_id:?}"); parent } pub fn body_owner_def_id(self, BodyId { hir_id }: BodyId) -> LocalDefId { - let parent = self.parent_id(hir_id); - associated_body(self.tcx.hir_node(parent)).unwrap().0 + associated_body(self.tcx.parent_hir_node(hir_id)).unwrap().0 } /// Given a `LocalDefId`, returns the `BodyId` associated with it, @@ -569,8 +557,8 @@ impl<'hir> Map<'hir> { /// Checks if the node is left-hand side of an assignment. pub fn is_lhs(self, id: HirId) -> bool { - match self.find_parent(id) { - Some(Node::Expr(expr)) => match expr.kind { + match self.tcx.parent_hir_node(id) { + Node::Expr(expr) => match expr.kind { ExprKind::Assign(lhs, _rhs, _span) => lhs.hir_id == id, _ => false, }, @@ -793,7 +781,7 @@ impl<'hir> Map<'hir> { Node::Pat(&Pat { kind: PatKind::Binding(_, _, ident, _), .. }) => Some(ident), // A `Ctor` doesn't have an identifier itself, but its parent // struct/variant does. Compare with `hir::Map::span`. - Node::Ctor(..) => match self.find_parent(id)? { + Node::Ctor(..) => match self.tcx.parent_hir_node(id) { Node::Item(item) => Some(item.ident), Node::Variant(variant) => Some(variant.ident), _ => unreachable!(), @@ -925,7 +913,7 @@ impl<'hir> Map<'hir> { ForeignItemKind::Fn(decl, _, _) => until_within(item.span, decl.output.span()), _ => named_span(item.span, item.ident, None), }, - Node::Ctor(_) => return self.span(self.parent_id(hir_id)), + Node::Ctor(_) => return self.span(self.tcx.parent_hir_id(hir_id)), Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl_span, .. }), span, @@ -968,7 +956,7 @@ impl<'hir> Map<'hir> { Node::PatField(field) => field.span, Node::Arm(arm) => arm.span, Node::Block(block) => block.span, - Node::Ctor(..) => self.span_with_body(self.parent_id(hir_id)), + Node::Ctor(..) => self.span_with_body(self.tcx.parent_hir_id(hir_id)), Node::Lifetime(lifetime) => lifetime.ident.span, Node::GenericParam(param) => param.span, Node::Infer(i) => i.span, @@ -1001,7 +989,7 @@ impl<'hir> Map<'hir> { /// Returns the HirId of `N` in `struct Foo` when /// called with the HirId for the `{ ... }` anon const pub fn opt_const_param_default_param_def_id(self, anon_const: HirId) -> Option { - match self.get_parent(anon_const) { + match self.tcx.parent_hir_node(anon_const) { Node::GenericParam(GenericParam { def_id: param_id, kind: GenericParamKind::Const { .. }, @@ -1026,7 +1014,7 @@ impl<'hir> Map<'hir> { _ => None, }?; - match self.find_parent(expr.hir_id)? { + match self.tcx.parent_hir_node(expr.hir_id) { Node::ExprField(field) => { if field.ident.name == local.name && field.is_shorthand { return Some(local.name); diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index f66cd2370e3e..4ef9bc16221f 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -136,13 +136,14 @@ pub fn provide(providers: &mut Providers) { }; providers.opt_hir_owner_nodes = |tcx, id| tcx.hir_crate(()).owners.get(id)?.as_owner().map(|i| &i.nodes); - providers.hir_owner_parent = |tcx, id| { - // Accessing the local_parent is ok since its value is hashed as part of `id`'s DefPathHash. - tcx.opt_local_parent(id.def_id).map_or(CRATE_HIR_ID, |parent| { - let mut parent_hir_id = tcx.local_def_id_to_hir_id(parent); - parent_hir_id.local_id = - tcx.hir_crate(()).owners[parent_hir_id.owner.def_id].unwrap().parenting[&id.def_id]; - parent_hir_id + providers.hir_owner_parent = |tcx, owner_id| { + tcx.opt_local_parent(owner_id.def_id).map_or(CRATE_HIR_ID, |parent_def_id| { + let parent_owner_id = tcx.local_def_id_to_hir_id(parent_def_id).owner; + HirId { + owner: parent_owner_id, + local_id: tcx.hir_crate(()).owners[parent_owner_id.def_id].unwrap().parenting + [&owner_id.def_id], + } }) }; providers.hir_attrs = |tcx, id| { diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 66f448a451ee..a1cdc794749c 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -356,6 +356,8 @@ pub enum UndefinedBehaviorInfo<'tcx> { UninhabitedEnumVariantWritten(VariantIdx), /// An uninhabited enum variant is projected. UninhabitedEnumVariantRead(VariantIdx), + /// Trying to set discriminant to the niched variant, but the value does not match. + InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> }, /// ABI-incompatible argument types. AbiMismatchArgument { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> }, /// ABI-incompatible return types. @@ -468,7 +470,7 @@ pub enum UnsupportedOpInfo { /// Accessing thread local statics ThreadLocalStatic(DefId), /// Accessing an unsupported extern static. - ReadExternStatic(DefId), + ExternStatic(DefId), } /// Error information for when the program exhausted the resources granted to it diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 13cc5cbed443..7cb326ce696a 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -358,11 +358,17 @@ pub fn suggest_constraining_type_params<'a>( // trait Foo {... } // - insert: `where T: Zar` if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) { + // If we are here and the where clause span is of non-zero length + // it means we're dealing with an empty where clause like this: + // fn foo(x: X) where { ... } + // In that case we don't want to add another "where" (Fixes #120838) + let where_prefix = if generics.where_clause_span.is_empty() { " where" } else { "" }; + // Suggest a bound, but there is no existing `where` clause *and* the type param has a // default (``), so we suggest adding `where T: Bar`. suggestions.push(( generics.tail_span_for_predicate_suggestion(), - format!(" where {param_name}: {constraint}"), + format!("{where_prefix} {param_name}: {constraint}"), SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }, )); continue; diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index f90703e61844..5cf90e94907b 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -877,7 +877,24 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::CoroutineClosure(did, args) => { p!(write("{{")); if !self.should_print_verbose() { - p!(write("coroutine-closure")); + match self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(did)).unwrap() + { + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Async, + hir::CoroutineSource::Closure, + ) => p!("async closure"), + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::AsyncGen, + hir::CoroutineSource::Closure, + ) => p!("async gen closure"), + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Gen, + hir::CoroutineSource::Closure, + ) => p!("gen closure"), + _ => unreachable!( + "coroutine from coroutine-closure should have CoroutineSource::Closure" + ), + } // FIXME(eddyb) should use `def_span`. if let Some(did) = did.as_local() { if self.tcx().sess.opts.unstable_opts.span_free_formats { diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index f2321e7e1d22..303f285b00c7 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -435,7 +435,10 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>( Ok(a) } - (ty::Param(a_p), ty::Param(b_p)) if a_p.index == b_p.index => Ok(a), + (ty::Param(a_p), ty::Param(b_p)) if a_p.index == b_p.index => { + debug_assert_eq!(a_p.name, b_p.name, "param types with same index differ in name"); + Ok(a) + } (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a), @@ -593,7 +596,10 @@ pub fn structurally_relate_consts<'tcx, R: TypeRelation<'tcx>>( (ty::ConstKind::Error(_), _) => return Ok(a), (_, ty::ConstKind::Error(_)) => return Ok(b), - (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) => a_p.index == b_p.index, + (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index == b_p.index => { + debug_assert_eq!(a_p.name, b_p.name, "param types with same index differ in name"); + true + } (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2, (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 2d3669487718..04740a962915 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -792,7 +792,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { return id; } - let next = hir.parent_id(id); + let next = self.tcx.parent_hir_id(id); if next == id { bug!("lint traversal reached the root of the crate"); } diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 6a37047a6938..86e99a8a5b5c 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -2,7 +2,9 @@ //! //! Currently, this pass only propagates scalar values. -use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable}; +use rustc_const_eval::interpret::{ + ImmTy, Immediate, InterpCx, OpTy, PlaceTy, PointerArithmetic, Projectable, +}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult, Scalar}; @@ -936,12 +938,50 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm } fn binary_ptr_op( - _ecx: &InterpCx<'mir, 'tcx, Self>, - _bin_op: BinOp, - _left: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>, - _right: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>, + ecx: &InterpCx<'mir, 'tcx, Self>, + bin_op: BinOp, + left: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>, + right: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>, ) -> interpret::InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)> { - throw_machine_stop_str!("can't do pointer arithmetic"); + use rustc_middle::mir::BinOp::*; + Ok(match bin_op { + Eq | Ne | Lt | Le | Gt | Ge => { + // Types can differ, e.g. fn ptrs with different `for`. + assert_eq!(left.layout.abi, right.layout.abi); + let size = ecx.pointer_size(); + // Just compare the bits. ScalarPairs are compared lexicographically. + // We thus always compare pairs and simply fill scalars up with 0. + // If the pointer has provenance, `to_bits` will return `Err` and we bail out. + let left = match **left { + Immediate::Scalar(l) => (l.to_bits(size)?, 0), + Immediate::ScalarPair(l1, l2) => (l1.to_bits(size)?, l2.to_bits(size)?), + Immediate::Uninit => panic!("we should never see uninit data here"), + }; + let right = match **right { + Immediate::Scalar(r) => (r.to_bits(size)?, 0), + Immediate::ScalarPair(r1, r2) => (r1.to_bits(size)?, r2.to_bits(size)?), + Immediate::Uninit => panic!("we should never see uninit data here"), + }; + let res = match bin_op { + Eq => left == right, + Ne => left != right, + Lt => left < right, + Le => left <= right, + Gt => left > right, + Ge => left >= right, + _ => bug!(), + }; + (ImmTy::from_bool(res, *ecx.tcx), false) + } + + // Some more operations are possible with atomics. + // The return value always has the provenance of the *left* operand. + Add | Sub | BitOr | BitAnd | BitXor => { + throw_machine_stop_str!("pointer arithmetic is not handled") + } + + _ => span_bug!(ecx.cur_span(), "Invalid operator on pointers: {:?}", bin_op), + }) } fn expose_ptr( diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 36c441a39453..2c7ae53055f7 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -93,7 +93,6 @@ use rustc_index::IndexVec; use rustc_middle::mir::interpret::GlobalAlloc; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; -use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut}; use rustc_span::def_id::DefId; @@ -552,6 +551,29 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } value.offset(Size::ZERO, to, &self.ecx).ok()? } + CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize) => { + let src = self.evaluated[value].as_ref()?; + let to = self.ecx.layout_of(to).ok()?; + let dest = self.ecx.allocate(to, MemoryKind::Stack).ok()?; + self.ecx.unsize_into(src, to, &dest.clone().into()).ok()?; + self.ecx + .alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id()) + .ok()?; + dest.into() + } + CastKind::FnPtrToPtr + | CastKind::PtrToPtr + | CastKind::PointerCoercion( + ty::adjustment::PointerCoercion::MutToConstPointer + | ty::adjustment::PointerCoercion::ArrayToPointer + | ty::adjustment::PointerCoercion::UnsafeFnPointer, + ) => { + let src = self.evaluated[value].as_ref()?; + let src = self.ecx.read_immediate(src).ok()?; + let to = self.ecx.layout_of(to).ok()?; + let ret = self.ecx.ptr_to_ptr(&src, to).ok()?; + ret.into() + } _ => return None, }, }; @@ -778,18 +800,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Operations. Rvalue::Len(ref mut place) => return self.simplify_len(place, location), - Rvalue::Cast(kind, ref mut value, to) => { - let from = value.ty(self.local_decls, self.tcx); - let value = self.simplify_operand(value, location)?; - if let CastKind::PointerCoercion( - PointerCoercion::ReifyFnPointer | PointerCoercion::ClosureFnPointer(_), - ) = kind - { - // Each reification of a generic fn may get a different pointer. - // Do not try to merge them. - return self.new_opaque(); - } - Value::Cast { kind, value, from, to } + Rvalue::Cast(ref mut kind, ref mut value, to) => { + return self.simplify_cast(kind, value, to, location); } Rvalue::BinaryOp(op, box (ref mut lhs, ref mut rhs)) => { let ty = lhs.ty(self.local_decls, self.tcx); @@ -1035,6 +1047,50 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } + fn simplify_cast( + &mut self, + kind: &mut CastKind, + operand: &mut Operand<'tcx>, + to: Ty<'tcx>, + location: Location, + ) -> Option { + use rustc_middle::ty::adjustment::PointerCoercion::*; + use CastKind::*; + + let mut from = operand.ty(self.local_decls, self.tcx); + let mut value = self.simplify_operand(operand, location)?; + if from == to { + return Some(value); + } + + if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_)) = kind { + // Each reification of a generic fn may get a different pointer. + // Do not try to merge them. + return self.new_opaque(); + } + + if let PtrToPtr | PointerCoercion(MutToConstPointer) = kind + && let Value::Cast { kind: inner_kind, value: inner_value, from: inner_from, to: _ } = + *self.get(value) + && let PtrToPtr | PointerCoercion(MutToConstPointer) = inner_kind + { + from = inner_from; + value = inner_value; + *kind = PtrToPtr; + if inner_from == to { + return Some(inner_value); + } + if let Some(const_) = self.try_as_constant(value) { + *operand = Operand::Constant(Box::new(const_)); + } else if let Some(local) = self.try_as_local(value, location) { + *operand = Operand::Copy(local.into()); + self.reused_locals.insert(local); + } + } + + Some(self.insert(Value::Cast { kind: *kind, value, from, to })) + } + fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option { // Trivial case: we are fetching a statically known length. let place_ty = place.ty(self.local_decls, self.tcx).ty; diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 7b6de3a54395..860d280be295 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -447,16 +447,13 @@ fn build_thread_local_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'t fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Body<'tcx> { debug!("build_clone_shim(def_id={:?})", def_id); - let param_env = tcx.param_env_reveal_all_normalized(def_id); - let mut builder = CloneShimBuilder::new(tcx, def_id, self_ty); - let is_copy = self_ty.is_copy_modulo_regions(tcx, param_env); let dest = Place::return_place(); let src = tcx.mk_place_deref(Place::from(Local::new(1 + 0))); match self_ty.kind() { - _ if is_copy => builder.copy_shim(), + ty::FnDef(..) | ty::FnPtr(_) => builder.copy_shim(), ty::Closure(_, args) => builder.tuple_like_shim(dest, src, args.as_closure().upvar_tys()), ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()), ty::Coroutine(coroutine_def_id, args) => { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 3376af986531..149e4c2cb08e 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -666,7 +666,15 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { debug!(?def_id, ?fn_span); for arg in args { - if let Some(too_large_size) = self.operand_size_if_too_large(limit, &arg.node) { + // Moving args into functions is typically implemented with pointer + // passing at the llvm-ir level and not by memcpy's. So always allow + // moving args into functions. + let operand: &mir::Operand<'tcx> = &arg.node; + if let mir::Operand::Move(_) = operand { + continue; + } + + if let Some(too_large_size) = self.operand_size_if_too_large(limit, operand) { self.lint_large_assignment(limit.0, too_large_size, location, arg.span); }; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index ec704dec352d..c24827ea902d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2069,14 +2069,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: Span, target: Target, ) -> bool { - let hir = self.tcx.hir(); - if let Target::ForeignFn = target - && let Some(parent) = hir.opt_parent_id(hir_id) && let hir::Node::Item(Item { kind: ItemKind::ForeignMod { abi: Abi::RustIntrinsic | Abi::PlatformIntrinsic, .. }, .. - }) = self.tcx.hir_node(parent) + }) = self.tcx.parent_hir_node(hir_id) { return true; } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 7d95a0bc4782..49ae9f16c8e5 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -625,6 +625,13 @@ impl Span { span.lo < other.hi && other.lo < span.hi } + /// Returns `true` if `self` touches or adjoins `other`. + pub fn overlaps_or_adjacent(self, other: Span) -> bool { + let span = self.data(); + let other = other.data(); + span.lo <= other.hi && other.lo <= span.hi + } + /// Returns `true` if the spans are equal with regards to the source text. /// /// Use this instead of `==` when either span could be generated code, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index a25bb98215e6..c8f665e130bb 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1032,15 +1032,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } }; - let hir = self.tcx.hir(); let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?); - match hir.find_parent(hir_id) { - Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. })) => { + match self.tcx.parent_hir_node(hir_id) { + hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. }) => { get_name(err, &local.pat.kind) } // Different to previous arm because one is `&hir::Local` and the other // is `P`. - Some(hir::Node::Local(local)) => get_name(err, &local.pat.kind), + hir::Node::Local(local) => get_name(err, &local.pat.kind), _ => None, } } @@ -1202,8 +1201,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else { return; }; - let Some(hir::Node::Local(hir::Local { ty: None, init: Some(init), .. })) = - self.tcx.hir().find_parent(pat.hir_id) + let hir::Node::Local(hir::Local { ty: None, init: Some(init), .. }) = + self.tcx.parent_hir_node(pat.hir_id) else { return; }; @@ -1790,7 +1789,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let Res::Local(hir_id) = path.res && let hir::Node::Pat(binding) = self.tcx.hir_node(hir_id) - && let Some(hir::Node::Local(local)) = self.tcx.hir().find_parent(binding.hir_id) + && let hir::Node::Local(local) = self.tcx.parent_hir_node(binding.hir_id) && let None = local.ty && let Some(binding_expr) = local.init { @@ -3188,8 +3187,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } ObligationCauseCode::VariableType(hir_id) => { - let parent_node = tcx.hir().parent_id(hir_id); - match tcx.hir_node(parent_node) { + match tcx.parent_hir_node(hir_id) { Node::Local(hir::Local { ty: Some(ty), .. }) => { err.span_suggestion_verbose( ty.span.shrink_to_lo(), @@ -3237,8 +3235,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { types always have a known size"; if let Some(hir_id) = hir_id && let hir::Node::Param(param) = self.tcx.hir_node(hir_id) - && let Some(item) = self.tcx.hir().find_parent(hir_id) - && let Some(decl) = item.fn_decl() + && let Some(decl) = self.tcx.parent_hir_node(hir_id).fn_decl() && let Some(t) = decl.inputs.iter().find(|t| param.ty_span.contains(t.span)) { // We use `contains` because the type might be surrounded by parentheses, @@ -4079,8 +4076,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let hir::Path { res: Res::Local(hir_id), .. } = path && let hir::Node::Pat(binding) = self.tcx.hir_node(*hir_id) - && let parent_hir_id = self.tcx.hir().parent_id(binding.hir_id) - && let hir::Node::Local(local) = self.tcx.hir_node(parent_hir_id) + && let hir::Node::Local(local) = self.tcx.parent_hir_node(binding.hir_id) && let Some(binding_expr) = local.init { // If the expression we're calling on is a binding, we want to point at the @@ -4338,8 +4334,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let hir::Path { res: Res::Local(hir_id), .. } = path && let hir::Node::Pat(binding) = self.tcx.hir_node(*hir_id) - && let Some(parent) = self.tcx.hir().find_parent(binding.hir_id) { + let parent = self.tcx.parent_hir_node(binding.hir_id); // We've reached the root of the method call chain... if let hir::Node::Local(local) = parent && let Some(binding_expr) = local.init diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index fa02c0ab99a6..5e5379d96492 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -1162,8 +1162,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path && let hir::Node::Pat(binding) = self.tcx.hir_node(*hir_id) - && let Some(parent) = self.tcx.hir().find_parent(binding.hir_id) { + let parent = self.tcx.parent_hir_node(binding.hir_id); // We've reached the root of the method call chain... if let hir::Node::Local(local) = parent && let Some(binding_expr) = local.init diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index eae80199ce56..7fa416197b35 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -209,10 +209,8 @@ fn resolve_associated_item<'tcx>( let name = tcx.item_name(trait_item_id); if name == sym::clone { let self_ty = trait_ref.self_ty(); - - let is_copy = self_ty.is_copy_modulo_regions(tcx, param_env); match self_ty.kind() { - _ if is_copy => (), + ty::FnDef(..) | ty::FnPtr(_) => (), ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::Closure(..) diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index f83c8f83cc98..3b29c144a89f 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -67,6 +67,26 @@ impl ThinBox { let ptr = WithOpaqueHeader::new(meta, value); ThinBox { ptr, _marker: PhantomData } } + + /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on + /// the stack. Returns an error if allocation fails, instead of aborting. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// #![feature(thin_box)] + /// use std::boxed::ThinBox; + /// + /// let five = ThinBox::try_new(5)?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + /// + /// [`Metadata`]: core::ptr::Pointee::Metadata + pub fn try_new(value: T) -> Result { + let meta = ptr::metadata(&value); + WithOpaqueHeader::try_new(meta, value).map(|ptr| ThinBox { ptr, _marker: PhantomData }) + } } #[unstable(feature = "thin_box", issue = "92791")] @@ -179,6 +199,10 @@ impl WithOpaqueHeader { let ptr = WithHeader::new(header, value); Self(ptr.0) } + + fn try_new(header: H, value: T) -> Result { + WithHeader::try_new(header, value).map(|ptr| Self(ptr.0)) + } } impl WithHeader { @@ -224,6 +248,46 @@ impl WithHeader { } } + /// Non-panicking version of `new`. + /// Any error is returned as `Err(core::alloc::AllocError)`. + fn try_new(header: H, value: T) -> Result, core::alloc::AllocError> { + let value_layout = Layout::new::(); + let Ok((layout, value_offset)) = Self::alloc_layout(value_layout) else { + return Err(core::alloc::AllocError); + }; + + unsafe { + // Note: It's UB to pass a layout with a zero size to `alloc::alloc`, so + // we use `layout.dangling()` for this case, which should have a valid + // alignment for both `T` and `H`. + let ptr = if layout.size() == 0 { + // Some paranoia checking, mostly so that the ThinBox tests are + // more able to catch issues. + debug_assert!( + value_offset == 0 && mem::size_of::() == 0 && mem::size_of::() == 0 + ); + layout.dangling() + } else { + let ptr = alloc::alloc(layout); + if ptr.is_null() { + return Err(core::alloc::AllocError); + } + + // Safety: + // - The size is at least `aligned_header_size`. + let ptr = ptr.add(value_offset) as *mut _; + + NonNull::new_unchecked(ptr) + }; + + let result = WithHeader(ptr, PhantomData); + ptr::write(result.header(), header); + ptr::write(result.value().cast(), value); + + Ok(result) + } + } + // Safety: // - Assumes that either `value` can be dereferenced, or is the // `NonNull::dangling()` we use when both `T` and `H` are ZSTs. diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index 5b50ef7bf6c2..fce2585cbf5c 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -188,6 +188,10 @@ //! * `#X` - precedes the argument with a `0x` //! * `#b` - precedes the argument with a `0b` //! * `#o` - precedes the argument with a `0o` +//! +//! See [Formatting traits](#formatting-traits) for a description of what the `?`, `x`, `X`, +//! `b`, and `o` flags do. +//! //! * `0` - This is used to indicate for integer formats that the padding to `width` should //! both be done with a `0` character as well as be sign-aware. A format //! like `{:08}` would yield `00000001` for the integer `1`, while the @@ -197,6 +201,7 @@ //! and before the digits. When used together with the `#` flag, a similar //! rule applies: padding zeros are inserted after the prefix but before //! the digits. The prefix is included in the total width. +//! This flag overrides the [fill character and alignment flag](#fillalignment). //! //! ## Precision //! diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 030040ba09ab..c77fa371cc74 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -237,9 +237,9 @@ use crate::cmp::Ordering; use crate::fmt::{self, Debug, Display}; -use crate::intrinsics::is_nonoverlapping; +use crate::intrinsics; use crate::marker::{PhantomData, Unsize}; -use crate::mem; +use crate::mem::{self, size_of}; use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn}; use crate::ptr::{self, NonNull}; @@ -435,11 +435,15 @@ impl Cell { #[inline] #[stable(feature = "move_cell", since = "1.17.0")] pub fn swap(&self, other: &Self) { + fn is_nonoverlapping(src: *const T, dst: *const T) -> bool { + intrinsics::is_nonoverlapping(src.cast(), dst.cast(), size_of::(), 1) + } + if ptr::eq(self, other) { // Swapping wouldn't change anything. return; } - if !is_nonoverlapping(self, other, 1) { + if !is_nonoverlapping(self, other) { // See for why we need to stop here. panic!("`Cell::swap` on overlapping non-identical `Cell`s"); } diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index bb839a71e90e..248943cf0226 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -88,6 +88,7 @@ use crate::str; // want `repr(transparent)` but we don't want it to show up in rustdoc, so we hide it under // `cfg(doc)`. This is an ad-hoc implementation of attribute privacy. #[cfg_attr(not(doc), repr(transparent))] +#[allow(clippy::derived_hash_with_manual_eq)] pub struct CStr { // FIXME: this should not be represented with a DST slice but rather with // just a raw `c_char` along with some form of marker to make diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 18c82f97c28e..c8259c0024c7 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -56,7 +56,7 @@ use crate::marker::DiscriminantKind; use crate::marker::Tuple; -use crate::mem::{self, align_of}; +use crate::mem::align_of; pub mod mir; pub mod simd; @@ -1027,7 +1027,7 @@ extern "rust-intrinsic" { /// The size of the referenced value in bytes. /// - /// The stabilized version of this intrinsic is [`mem::size_of_val`]. + /// The stabilized version of this intrinsic is [`crate::mem::size_of_val`]. #[rustc_const_unstable(feature = "const_size_of_val", issue = "46571")] #[rustc_nounwind] pub fn size_of_val(_: *const T) -> usize; @@ -1107,7 +1107,7 @@ extern "rust-intrinsic" { /// Moves a value out of scope without running drop glue. /// - /// This exists solely for [`mem::forget_unsized`]; normal `forget` uses + /// This exists solely for [`crate::mem::forget_unsized`]; normal `forget` uses /// `ManuallyDrop` instead. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -1233,7 +1233,7 @@ extern "rust-intrinsic" { /// Depending on what the code is doing, the following alternatives are preferable to /// pointer-to-integer transmutation: /// - If the code just wants to store data of arbitrary type in some buffer and needs to pick a - /// type for that buffer, it can use [`MaybeUninit`][mem::MaybeUninit]. + /// type for that buffer, it can use [`MaybeUninit`][crate::mem::MaybeUninit]. /// - If the code actually wants to work on the address the pointer points to, it can use `as` /// casts or [`ptr.addr()`][pointer::addr]. /// @@ -2317,7 +2317,7 @@ extern "rust-intrinsic" { /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// - /// The to-be-stabilized version of this intrinsic is [`mem::variant_count`]. + /// The to-be-stabilized version of this intrinsic is [`crate::mem::variant_count`]. #[rustc_const_unstable(feature = "variant_count", issue = "73662")] #[rustc_safe_intrinsic] #[rustc_nounwind] @@ -2569,6 +2569,19 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn is_val_statically_known(arg: T) -> bool; + /// Returns the value of `cfg!(debug_assertions)`, but after monomorphization instead of in + /// macro expansion. + /// + /// This always returns `false` in const eval and Miri. The interpreter provides better + /// diagnostics than the checks that this is used to implement. However, this means + /// you should only be using this intrinsic to guard requirements that, if violated, + /// immediately lead to UB. Otherwise, const-eval and Miri will miss out on those + /// checks entirely. + /// + /// Since this is evaluated after monomorphization, branching on this value can be used to + /// implement debug assertions that are included in the precompiled standard library, but can + /// be optimized out by builds that monomorphize the standard library code with debug + /// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`]. #[rustc_const_unstable(feature = "delayed_debug_assertions", issue = "none")] #[rustc_safe_intrinsic] #[cfg(not(bootstrap))] @@ -2597,7 +2610,7 @@ pub(crate) const fn debug_assertions() -> bool { /// These checks are behind a condition which is evaluated at codegen time, not expansion time like /// [`debug_assert`]. This means that a standard library built with optimizations and debug /// assertions disabled will have these checks optimized out of its monomorphizations, but if a -/// a caller of the standard library has debug assertions enabled and monomorphizes an expansion of +/// caller of the standard library has debug assertions enabled and monomorphizes an expansion of /// this macro, that monomorphization will contain the check. /// /// Since these checks cannot be optimized out in MIR, some care must be taken in both call and @@ -2606,8 +2619,8 @@ pub(crate) const fn debug_assertions() -> bool { /// combination of properties ensures that the code for the checks is only compiled once, and has a /// minimal impact on the caller's code size. /// -/// Caller should also introducing any other `let` bindings or any code outside this macro in order -/// to call it. Since the precompiled standard library is built with full debuginfo and these +/// Callers should also avoid introducing any other `let` bindings or any code outside this macro in +/// order to call it. Since the precompiled standard library is built with full debuginfo and these /// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough /// debuginfo to have a measurable compile-time impact on debug builds. /// @@ -2659,12 +2672,9 @@ pub(crate) fn is_valid_allocation_size(size: usize, len: usize) -> bool { len <= max_len } -pub(crate) fn is_nonoverlapping_mono( - src: *const (), - dst: *const (), - size: usize, - count: usize, -) -> bool { +/// Checks whether the regions of memory starting at `src` and `dst` of size +/// `count * size` do *not* overlap. +pub(crate) fn is_nonoverlapping(src: *const (), dst: *const (), size: usize, count: usize) -> bool { let src_usize = src.addr(); let dst_usize = dst.addr(); let Some(size) = size.checked_mul(count) else { @@ -2678,24 +2688,6 @@ pub(crate) fn is_nonoverlapping_mono( diff >= size } -/// Checks whether the regions of memory starting at `src` and `dst` of size -/// `count * size_of::()` do *not* overlap. -#[inline] -pub(crate) fn is_nonoverlapping(src: *const T, dst: *const T, count: usize) -> bool { - let src_usize = src.addr(); - let dst_usize = dst.addr(); - let Some(size) = mem::size_of::().checked_mul(count) else { - // Use panic_nounwind instead of Option::expect, so that this function is nounwind. - crate::panicking::panic_nounwind( - "is_nonoverlapping: `size_of::() * count` overflows a usize", - ) - }; - let diff = src_usize.abs_diff(dst_usize); - // If the absolute distance between the ptrs is at least as big as the size of the buffer, - // they do not overlap. - diff >= size -} - /// Copies `count * size_of::()` bytes from `src` to `dst`. The source /// and destination must *not* overlap. /// @@ -2809,7 +2801,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us ) => is_aligned_and_not_null(src, align) && is_aligned_and_not_null(dst, align) - && is_nonoverlapping_mono(src, dst, size, count) + && is_nonoverlapping(src, dst, size, count) ); copy_nonoverlapping(src, dst, count) } diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index 0fd27974dcec..ef4c65639eb4 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -190,14 +190,27 @@ extern "platform-intrinsic" { /// /// `T` must be a vector. /// - /// `U` must be a const array of `i32`s. + /// `U` must be a **const** array of `i32`s. This means it must either refer to a named + /// const or be given as an inline const expression (`const { ... }`). /// /// `V` must be a vector with the same element type as `T` and the same length as `U`. /// - /// Concatenates `x` and `y`, then returns a new vector such that each element is selected from - /// the concatenation by the matching index in `idx`. + /// Returns a new vector such that element `i` is selected from `xy[idx[i]]`, where `xy` + /// is the concatenation of `x` and `y`. It is a compile-time error if `idx[i]` is out-of-bounds + /// of `xy`. pub fn simd_shuffle(x: T, y: T, idx: U) -> V; + /// Shuffle two vectors by const indices. + /// + /// `T` must be a vector. + /// + /// `U` must be a vector with the same element type as `T` and the same length as `IDX`. + /// + /// Returns a new vector such that element `i` is selected from `xy[IDX[i]]`, where `xy` + /// is the concatenation of `x` and `y`. It is a compile-time error if `IDX[i]` is out-of-bounds + /// of `xy`. + pub fn simd_shuffle_generic(x: T, y: T) -> U; + /// Read a vector of pointers. /// /// `T` must be a vector. @@ -232,6 +245,9 @@ extern "platform-intrinsic" { /// corresponding value in `val` to the pointer. /// Otherwise if the corresponding value in `mask` is `0`, do nothing. /// + /// The stores happen in left-to-right order. + /// (This is relevant in case two of the stores overlap.) + /// /// # Safety /// Unmasked values in `T` must be writeable as if by `::write` (e.g. aligned to the element /// type). @@ -468,4 +484,36 @@ extern "platform-intrinsic" { /// /// `T` must be a vector of integers. pub fn simd_cttz(x: T) -> T; + + /// Round up each element to the next highest integer-valued float. + /// + /// `T` must be a vector of floats. + pub fn simd_ceil(x: T) -> T; + + /// Round down each element to the next lowest integer-valued float. + /// + /// `T` must be a vector of floats. + pub fn simd_floor(x: T) -> T; + + /// Round each element to the closest integer-valued float. + /// Ties are resolved by rounding away from 0. + /// + /// `T` must be a vector of floats. + pub fn simd_round(x: T) -> T; + + /// Return the integer part of each element as an integer-valued float. + /// In other words, non-integer values are truncated towards zero. + /// + /// `T` must be a vector of floats. + pub fn simd_trunc(x: T) -> T; + + /// Takes the square root of each element. + /// + /// `T` must be a vector of floats. + pub fn simd_fsqrt(x: T) -> T; + + /// Computes `(x*y) + z` for each element, but without any intermediate rounding. + /// + /// `T` must be a vector of floats. + pub fn simd_fma(x: T, y: T, z: T) -> T; } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 709eba2ffc9a..047cb64ce506 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -462,6 +462,7 @@ impl f32 { /// and target platforms isn't guaranteed. #[stable(feature = "assoc_int_consts", since = "1.43.0")] #[rustc_diagnostic_item = "f32_nan"] + #[allow(clippy::eq_op)] pub const NAN: f32 = 0.0_f32 / 0.0_f32; /// Infinity (∞). #[stable(feature = "assoc_int_consts", since = "1.43.0")] @@ -483,6 +484,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] #[inline] + #[allow(clippy::eq_op)] // > if you intended to check if the operand is NaN, use `.is_nan()` instead :) pub const fn is_nan(self) -> bool { self != self } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 73fa61574cc4..16d819419355 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -461,6 +461,7 @@ impl f64 { /// and target platforms isn't guaranteed. #[rustc_diagnostic_item = "f64_nan"] #[stable(feature = "assoc_int_consts", since = "1.43.0")] + #[allow(clippy::eq_op)] pub const NAN: f64 = 0.0_f64 / 0.0_f64; /// Infinity (∞). #[stable(feature = "assoc_int_consts", since = "1.43.0")] @@ -482,6 +483,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] #[inline] + #[allow(clippy::eq_op)] // > if you intended to check if the operand is NaN, use `.is_nan()` instead :) pub const fn is_nan(self) -> bool { self != self } diff --git a/library/core/src/option.rs b/library/core/src/option.rs index d4c40c49934e..0e5eb03239ce 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -567,6 +567,7 @@ use crate::{ #[rustc_diagnostic_item = "Option"] #[lang = "Option"] #[stable(feature = "rust1", since = "1.0.0")] +#[allow(clippy::derived_hash_with_manual_eq)] // PartialEq is specialized pub enum Option { /// No value. #[lang = "None"] diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 85595b059ad9..eb3f93a6e965 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1660,6 +1660,11 @@ mod prim_ref {} /// * [`UnwindSafe`] /// * [`RefUnwindSafe`] /// +/// Note that while this type implements `PartialEq`, comparing function pointers is unreliable: +/// pointers to the same function can compare inequal (because functions are duplicated in multiple +/// codegen units), and pointers to *different* functions can compare equal (since identical +/// functions can be deduplicated within a codegen unit). +/// /// [`Hash`]: hash::Hash /// [`Pointer`]: fmt::Pointer /// [`UnwindSafe`]: panic::UnwindSafe diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index a6a390db043b..c7b0fe5694a7 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -175,6 +175,11 @@ impl Clone for PtrComponents { /// /// It is possible to name this struct with a type parameter that is not a `dyn` trait object /// (for example `DynMetadata`) but not to obtain a meaningful value of that struct. +/// +/// Note that while this type implements `PartialEq`, comparing vtable pointers is unreliable: +/// pointers to vtables of the same type for the same trait can compare inequal (because vtables are +/// duplicated in multiple codegen units), and pointers to vtables of *different* types/traits can +/// compare equal (since identical vtables can be deduplicated within a codegen unit). #[lang = "dyn_metadata"] pub struct DynMetadata { vtable_ptr: &'static VTable, diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 0fb9017e6d94..2bd14f357d80 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -376,12 +376,14 @@ //! [Stacked Borrows]: https://plv.mpi-sws.org/rustbelt/stacked-borrows/ #![stable(feature = "rust1", since = "1.0.0")] +// There are many unsafe functions taking pointers that don't dereference them. +#![allow(clippy::not_unsafe_ptr_arg_deref)] use crate::cmp::Ordering; use crate::fmt; use crate::hash; use crate::intrinsics::{ - self, assert_unsafe_precondition, is_aligned_and_not_null, is_nonoverlapping_mono, + self, assert_unsafe_precondition, is_aligned_and_not_null, is_nonoverlapping, }; use crate::marker::FnPtr; @@ -976,7 +978,7 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { ) => is_aligned_and_not_null(x, align) && is_aligned_and_not_null(y, align) - && is_nonoverlapping_mono(x, y, size, count) + && is_nonoverlapping(x, y, size, count) ); } diff --git a/library/core/src/str/validations.rs b/library/core/src/str/validations.rs index 2acef432f206..a11d7fee8af0 100644 --- a/library/core/src/str/validations.rs +++ b/library/core/src/str/validations.rs @@ -161,7 +161,7 @@ pub(super) const fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { // first E0 A0 80 last EF BF BF // excluding surrogates codepoints \u{d800} to \u{dfff} // ED A0 80 to ED BF BF - // 4-byte encoding is for codepoints \u{1000}0 to \u{10ff}ff + // 4-byte encoding is for codepoints \u{10000} to \u{10ffff} // first F0 90 80 80 last F4 8F BF BF // // Use the UTF-8 syntax from the RFC diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index d9654973b84e..1dec2bf40cc9 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -216,6 +216,10 @@ #![cfg_attr(not(target_has_atomic_load_store = "8"), allow(dead_code))] #![cfg_attr(not(target_has_atomic_load_store = "8"), allow(unused_imports))] #![rustc_diagnostic_item = "atomic_mod"] +// Clippy complains about the pattern of "safe function calling unsafe function taking pointers". +// This happens with AtomicPtr intrinsics but is fine, as the pointers clippy is concerned about +// are just normal values that get loaded/stored, but not dereferenced. +#![allow(clippy::not_unsafe_ptr_arg_deref)] use self::Ordering::*; diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 9ad71e394eac..09f3f2f02eab 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -99,6 +99,12 @@ impl RawWaker { /// [`RawWaker`] implementation. Calling one of the contained functions using /// any other `data` pointer will cause undefined behavior. /// +/// Note that while this type implements `PartialEq`, comparing function pointers, and hence +/// comparing structs like this that contain function pointers, is unreliable: pointers to the same +/// function can compare inequal (because functions are duplicated in multiple codegen units), and +/// pointers to *different* functions can compare equal (since identical functions can be +/// deduplicated within a codegen unit). +/// /// # Thread safety /// If the [`RawWaker`] will be used to construct a [`Waker`] then /// these functions must all be thread-safe (even though [`RawWaker`] is diff --git a/library/core/src/time.rs b/library/core/src/time.rs index e4f299429660..b533f5399387 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -28,6 +28,14 @@ const NANOS_PER_MILLI: u32 = 1_000_000; const NANOS_PER_MICRO: u32 = 1_000; const MILLIS_PER_SEC: u64 = 1_000; const MICROS_PER_SEC: u64 = 1_000_000; +#[unstable(feature = "duration_units", issue = "120301")] +const SECS_PER_MINUTE: u64 = 60; +#[unstable(feature = "duration_units", issue = "120301")] +const MINS_PER_HOUR: u64 = 60; +#[unstable(feature = "duration_units", issue = "120301")] +const HOURS_PER_DAY: u64 = 24; +#[unstable(feature = "duration_units", issue = "120301")] +const DAYS_PER_WEEK: u64 = 7; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] @@ -273,6 +281,11 @@ impl Duration { /// Creates a new `Duration` from the specified number of nanoseconds. /// + /// Note: Using this on the return value of `as_nanos()` might cause unexpected behavior: + /// `as_nanos()` returns a u128, and can return values that do not fit in u64, e.g. 585 years. + /// Instead, consider using the pattern `Duration::new(d.as_secs(), d.subsec_nanos())` + /// if you cannot copy/clone the Duration directly. + /// /// # Examples /// /// ``` @@ -291,6 +304,118 @@ impl Duration { Duration::new(nanos / (NANOS_PER_SEC as u64), (nanos % (NANOS_PER_SEC as u64)) as u32) } + /// Creates a new `Duration` from the specified number of weeks. + /// + /// # Panics + /// + /// Panics if the given number of weeks overflows the `Duration` size. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constructors)] + /// use std::time::Duration; + /// + /// let duration = Duration::from_weeks(4); + /// + /// assert_eq!(4 * 7 * 24 * 60 * 60, duration.as_secs()); + /// assert_eq!(0, duration.subsec_nanos()); + /// ``` + #[unstable(feature = "duration_constructors", issue = "120301")] + #[must_use] + #[inline] + pub const fn from_weeks(weeks: u64) -> Duration { + if weeks > u64::MAX / (SECS_PER_MINUTE * MINS_PER_HOUR * HOURS_PER_DAY * DAYS_PER_WEEK) { + panic!("overflow in Duration::from_days"); + } + + Duration::from_secs(weeks * MINS_PER_HOUR * SECS_PER_MINUTE * HOURS_PER_DAY * DAYS_PER_WEEK) + } + + /// Creates a new `Duration` from the specified number of days. + /// + /// # Panics + /// + /// Panics if the given number of days overflows the `Duration` size. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constructors)] + /// use std::time::Duration; + /// + /// let duration = Duration::from_days(7); + /// + /// assert_eq!(7 * 24 * 60 * 60, duration.as_secs()); + /// assert_eq!(0, duration.subsec_nanos()); + /// ``` + #[unstable(feature = "duration_constructors", issue = "120301")] + #[must_use] + #[inline] + pub const fn from_days(days: u64) -> Duration { + if days > u64::MAX / (SECS_PER_MINUTE * MINS_PER_HOUR * HOURS_PER_DAY) { + panic!("overflow in Duration::from_days"); + } + + Duration::from_secs(days * MINS_PER_HOUR * SECS_PER_MINUTE * HOURS_PER_DAY) + } + + /// Creates a new `Duration` from the specified number of hours. + /// + /// # Panics + /// + /// Panics if the given number of hours overflows the `Duration` size. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constructors)] + /// use std::time::Duration; + /// + /// let duration = Duration::from_hours(6); + /// + /// assert_eq!(6 * 60 * 60, duration.as_secs()); + /// assert_eq!(0, duration.subsec_nanos()); + /// ``` + #[unstable(feature = "duration_constructors", issue = "120301")] + #[must_use] + #[inline] + pub const fn from_hours(hours: u64) -> Duration { + if hours > u64::MAX / (SECS_PER_MINUTE * MINS_PER_HOUR) { + panic!("overflow in Duration::from_hours"); + } + + Duration::from_secs(hours * MINS_PER_HOUR * SECS_PER_MINUTE) + } + + /// Creates a new `Duration` from the specified number of minutes. + /// + /// # Panics + /// + /// Panics if the given number of minutes overflows the `Duration` size. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constructors)] + /// use std::time::Duration; + /// + /// let duration = Duration::from_mins(10); + /// + /// assert_eq!(10 * 60, duration.as_secs()); + /// assert_eq!(0, duration.subsec_nanos()); + /// ``` + #[unstable(feature = "duration_constructors", issue = "120301")] + #[must_use] + #[inline] + pub const fn from_mins(mins: u64) -> Duration { + if mins > u64::MAX / SECS_PER_MINUTE { + panic!("overflow in Duration::from_mins"); + } + + Duration::from_secs(mins * SECS_PER_MINUTE) + } + /// Returns true if this `Duration` spans no time. /// /// # Examples diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 44fac589d4c5..8e961d8adc37 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -154,18 +154,6 @@ macro_rules! maybe_tuple_doc { }; } -#[inline] -const fn ordering_is_some(c: Option, x: Ordering) -> bool { - // FIXME: Just use `==` once that's const-stable on `Option`s. - // This is mapping `None` to 2 and then doing the comparison afterwards - // because it optimizes better (`None::` is represented as 2). - x as i8 - == match c { - Some(c) => c as i8, - None => 2, - } -} - // Constructs an expression that performs a lexical ordering using method `$rel`. // The values are interleaved, so the macro invocation for // `(a1, a2, a3) < (b1, b2, b3)` would be `lexical_ord!(lt, opt_is_lt, a1, b1, @@ -176,7 +164,7 @@ const fn ordering_is_some(c: Option, x: Ordering) -> bool { macro_rules! lexical_ord { ($rel: ident, $ne_rel: ident, $a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {{ let c = PartialOrd::partial_cmp(&$a, &$b); - if !ordering_is_some(c, Equal) { ordering_is_some(c, $ne_rel) } + if c != Some(Equal) { c == Some($ne_rel) } else { lexical_ord!($rel, $ne_rel, $($rest_a, $rest_b),+) } }}; ($rel: ident, $ne_rel: ident, $a:expr, $b:expr) => { diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 6deebc0d2637..2fe79650dbfd 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -32,6 +32,7 @@ #![feature(duration_abs_diff)] #![feature(duration_consts_float)] #![feature(duration_constants)] +#![feature(duration_constructors)] #![feature(exact_size_is_empty)] #![feature(extern_types)] #![feature(flt2dec)] diff --git a/library/core/tests/time.rs b/library/core/tests/time.rs index 23f07bf84b3d..fe7bb11c6758 100644 --- a/library/core/tests/time.rs +++ b/library/core/tests/time.rs @@ -17,6 +17,49 @@ fn new_overflow() { let _ = Duration::new(u64::MAX, 1_000_000_000); } +#[test] +#[should_panic] +fn from_mins_overflow() { + let overflow = u64::MAX / 60 + 1; + let _ = Duration::from_mins(overflow); +} + +#[test] +#[should_panic] +fn from_hours_overflow() { + let overflow = u64::MAX / (60 * 60) + 1; + let _ = Duration::from_hours(overflow); +} + +#[test] +#[should_panic] +fn from_days_overflow() { + let overflow = u64::MAX / (24 * 60 * 60) + 1; + let _ = Duration::from_days(overflow); +} + +#[test] +#[should_panic] +fn from_weeks_overflow() { + let overflow = u64::MAX / (7 * 24 * 60 * 60) + 1; + let _ = Duration::from_weeks(overflow); +} + +#[test] +fn constructors() { + assert_eq!(Duration::from_weeks(1), Duration::from_secs(7 * 24 * 60 * 60)); + assert_eq!(Duration::from_weeks(0), Duration::ZERO); + + assert_eq!(Duration::from_days(1), Duration::from_secs(86_400)); + assert_eq!(Duration::from_days(0), Duration::ZERO); + + assert_eq!(Duration::from_hours(1), Duration::from_secs(3_600)); + assert_eq!(Duration::from_hours(0), Duration::ZERO); + + assert_eq!(Duration::from_mins(1), Duration::from_secs(60)); + assert_eq!(Duration::from_mins(0), Duration::ZERO); +} + #[test] fn secs() { assert_eq!(Duration::new(0, 0).as_secs(), 0); diff --git a/library/proc_macro/src/bridge/arena.rs b/library/proc_macro/src/bridge/arena.rs index fa72d2816ebf..c2b046ae41eb 100644 --- a/library/proc_macro/src/bridge/arena.rs +++ b/library/proc_macro/src/bridge/arena.rs @@ -102,6 +102,7 @@ impl Arena { } } + #[allow(clippy::mut_from_ref)] // arena allocator pub(crate) fn alloc_str<'a>(&'a self, string: &str) -> &'a mut str { let alloc = self.alloc_raw(string.len()); let bytes = MaybeUninit::write_slice(alloc, string.as_bytes()); diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 87e89a464bc1..90b76cbc26eb 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -201,6 +201,7 @@ impl ToString for TokenStream { /// `TokenTree::Punct`, or `TokenTree::Literal`. #[stable(feature = "proc_macro_lib", since = "1.15.0")] impl fmt::Display for TokenStream { + #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.to_string()) } @@ -776,6 +777,7 @@ impl ToString for TokenTree { /// `TokenTree::Punct`, or `TokenTree::Literal`. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl fmt::Display for TokenTree { + #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.to_string()) } @@ -906,6 +908,7 @@ impl ToString for Group { /// with `Delimiter::None` delimiters. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl fmt::Display for Group { + #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.to_string()) } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 571e475c3367..cab3e399ffa7 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -339,12 +339,14 @@ #![feature(portable_simd)] #![feature(prelude_2024)] #![feature(ptr_as_uninit)] +#![feature(ptr_mask)] #![feature(slice_internals)] #![feature(slice_ptr_get)] #![feature(slice_range)] #![feature(std_internals)] #![feature(str_internals)] #![feature(strict_provenance)] +#![feature(strict_provenance_atomic_ptr)] // tidy-alphabetical-end // // Library features (alloc): diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 24f2bdcf4217..a4c2dc8b1ed7 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -288,6 +288,7 @@ impl AsFd for fs::File { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedFd { + /// Takes ownership of a [`File`](fs::File)'s underlying file descriptor. #[inline] fn from(file: fs::File) -> OwnedFd { file.into_inner().into_inner().into_inner() @@ -296,6 +297,8 @@ impl From for OwnedFd { #[stable(feature = "io_safety", since = "1.63.0")] impl From for fs::File { + /// Returns a [`File`](fs::File) that takes ownership of the given + /// file descriptor. #[inline] fn from(owned_fd: OwnedFd) -> Self { Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned_fd))) @@ -312,6 +315,7 @@ impl AsFd for crate::net::TcpStream { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedFd { + /// Takes ownership of a [`TcpStream`](crate::net::TcpStream)'s socket file descriptor. #[inline] fn from(tcp_stream: crate::net::TcpStream) -> OwnedFd { tcp_stream.into_inner().into_socket().into_inner().into_inner().into() @@ -338,6 +342,7 @@ impl AsFd for crate::net::TcpListener { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedFd { + /// Takes ownership of a [`TcpListener`](crate::net::TcpListener)'s socket file descriptor. #[inline] fn from(tcp_listener: crate::net::TcpListener) -> OwnedFd { tcp_listener.into_inner().into_socket().into_inner().into_inner().into() @@ -364,6 +369,7 @@ impl AsFd for crate::net::UdpSocket { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedFd { + /// Takes ownership of a [`UdpSocket`](crate::net::UdpSocket)'s file descriptor. #[inline] fn from(udp_socket: crate::net::UdpSocket) -> OwnedFd { udp_socket.into_inner().into_socket().into_inner().into_inner().into() diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 34db54235f1c..3b7b610fdf96 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -1024,6 +1024,7 @@ impl AsFd for UnixDatagram { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedFd { + /// Takes ownership of a [`UnixDatagram`]'s socket file descriptor. #[inline] fn from(unix_datagram: UnixDatagram) -> OwnedFd { unsafe { OwnedFd::from_raw_fd(unix_datagram.into_raw_fd()) } diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index ecc0bbce5437..d64a43bc20ba 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -346,6 +346,7 @@ impl From for UnixListener { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedFd { + /// Takes ownership of a [`UnixListener`]'s socket file descriptor. #[inline] fn from(listener: UnixListener) -> OwnedFd { listener.0.into_inner().into_inner() diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 41290e0017ac..e117f616cafd 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -752,6 +752,7 @@ impl AsFd for UnixStream { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedFd { + /// Takes ownership of a [`UnixStream`]'s socket file descriptor. #[inline] fn from(unix_stream: UnixStream) -> OwnedFd { unsafe { OwnedFd::from_raw_fd(unix_stream.into_raw_fd()) } diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index ac551030492b..e45457b2e42b 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -362,6 +362,8 @@ impl FromRawFd for process::Stdio { #[stable(feature = "io_safety", since = "1.63.0")] impl From for process::Stdio { + /// Takes ownership of a file descriptor and returns a [`Stdio`](process::Stdio) + /// that can attach a stream to it. #[inline] fn from(fd: OwnedFd) -> process::Stdio { let fd = sys::fd::FileDesc::from_inner(fd); @@ -428,6 +430,7 @@ impl AsFd for crate::process::ChildStdin { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedFd { + /// Takes ownership of a [`ChildStdin`](crate::process::ChildStdin)'s file descriptor. #[inline] fn from(child_stdin: crate::process::ChildStdin) -> OwnedFd { child_stdin.into_inner().into_inner().into_inner() @@ -458,6 +461,7 @@ impl AsFd for crate::process::ChildStdout { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedFd { + /// Takes ownership of a [`ChildStdout`](crate::process::ChildStdout)'s file descriptor. #[inline] fn from(child_stdout: crate::process::ChildStdout) -> OwnedFd { child_stdout.into_inner().into_inner().into_inner() @@ -488,6 +492,7 @@ impl AsFd for crate::process::ChildStderr { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedFd { + /// Takes ownership of a [`ChildStderr`](crate::process::ChildStderr)'s file descriptor. #[inline] fn from(child_stderr: crate::process::ChildStderr) -> OwnedFd { child_stderr.into_inner().into_inner().into_inner() diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index b0540872c0b6..458c3bb036d8 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -502,6 +502,7 @@ impl AsHandle for fs::File { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedHandle { + /// Takes ownership of a [`File`](fs::File)'s underlying file handle. #[inline] fn from(file: fs::File) -> OwnedHandle { file.into_inner().into_inner().into_inner() @@ -510,6 +511,7 @@ impl From for OwnedHandle { #[stable(feature = "io_safety", since = "1.63.0")] impl From for fs::File { + /// Returns a [`File`](fs::File) that takes ownership of the given handle. #[inline] fn from(owned: OwnedHandle) -> Self { Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned))) @@ -574,6 +576,7 @@ impl AsHandle for crate::process::ChildStdin { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedHandle { + /// Takes ownership of a [`ChildStdin`](crate::process::ChildStdin)'s file handle. #[inline] fn from(child_stdin: crate::process::ChildStdin) -> OwnedHandle { unsafe { OwnedHandle::from_raw_handle(child_stdin.into_raw_handle()) } @@ -590,6 +593,7 @@ impl AsHandle for crate::process::ChildStdout { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedHandle { + /// Takes ownership of a [`ChildStdout`](crate::process::ChildStdout)'s file handle. #[inline] fn from(child_stdout: crate::process::ChildStdout) -> OwnedHandle { unsafe { OwnedHandle::from_raw_handle(child_stdout.into_raw_handle()) } @@ -606,6 +610,7 @@ impl AsHandle for crate::process::ChildStderr { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedHandle { + /// Takes ownership of a [`ChildStderr`](crate::process::ChildStderr)'s file handle. #[inline] fn from(child_stderr: crate::process::ChildStderr) -> OwnedHandle { unsafe { OwnedHandle::from_raw_handle(child_stderr.into_raw_handle()) } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 65f161f32e75..6ffdf907c8ed 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -319,6 +319,7 @@ impl AsSocket for crate::net::TcpStream { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedSocket { + /// Takes ownership of a [`TcpStream`](crate::net::TcpStream)'s socket. #[inline] fn from(tcp_stream: crate::net::TcpStream) -> OwnedSocket { unsafe { OwnedSocket::from_raw_socket(tcp_stream.into_raw_socket()) } @@ -343,6 +344,7 @@ impl AsSocket for crate::net::TcpListener { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedSocket { + /// Takes ownership of a [`TcpListener`](crate::net::TcpListener)'s socket. #[inline] fn from(tcp_listener: crate::net::TcpListener) -> OwnedSocket { unsafe { OwnedSocket::from_raw_socket(tcp_listener.into_raw_socket()) } @@ -367,6 +369,7 @@ impl AsSocket for crate::net::UdpSocket { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedSocket { + /// Takes ownership of a [`UdpSocket`](crate::net::UdpSocket)'s underlying socket. #[inline] fn from(udp_socket: crate::net::UdpSocket) -> OwnedSocket { unsafe { OwnedSocket::from_raw_socket(udp_socket.into_raw_socket()) } diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 5bf0154eae30..1be3acf5d436 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -24,6 +24,8 @@ impl FromRawHandle for process::Stdio { #[stable(feature = "io_safety", since = "1.63.0")] impl From for process::Stdio { + /// Takes ownership of a handle and returns a [`Stdio`](process::Stdio) + /// that can attach a stream to it. fn from(handle: OwnedHandle) -> process::Stdio { let handle = sys::handle::Handle::from_inner(handle); let io = sys::process::Stdio::Handle(handle); @@ -56,6 +58,7 @@ impl IntoRawHandle for process::Child { #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedHandle { + /// Takes ownership of a [`Child`](process::Child)'s process handle. fn from(child: process::Child) -> OwnedHandle { child.into_inner().into_handle().into_inner() } diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs new file mode 100644 index 000000000000..99df503b82de --- /dev/null +++ b/library/std/src/sys/cmath.rs @@ -0,0 +1,88 @@ +#![cfg(not(test))] + +// These symbols are all defined by `libm`, +// or by `compiler-builtins` on unsupported platforms. +extern "C" { + pub fn acos(n: f64) -> f64; + pub fn asin(n: f64) -> f64; + pub fn atan(n: f64) -> f64; + pub fn atan2(a: f64, b: f64) -> f64; + pub fn cbrt(n: f64) -> f64; + pub fn cbrtf(n: f32) -> f32; + pub fn cosh(n: f64) -> f64; + pub fn expm1(n: f64) -> f64; + pub fn expm1f(n: f32) -> f32; + pub fn fdim(a: f64, b: f64) -> f64; + pub fn fdimf(a: f32, b: f32) -> f32; + #[cfg_attr(target_env = "msvc", link_name = "_hypot")] + pub fn hypot(x: f64, y: f64) -> f64; + #[cfg_attr(target_env = "msvc", link_name = "_hypotf")] + pub fn hypotf(x: f32, y: f32) -> f32; + pub fn log1p(n: f64) -> f64; + pub fn log1pf(n: f32) -> f32; + pub fn sinh(n: f64) -> f64; + pub fn tan(n: f64) -> f64; + pub fn tanh(n: f64) -> f64; + pub fn tgamma(n: f64) -> f64; + pub fn tgammaf(n: f32) -> f32; + pub fn lgamma_r(n: f64, s: &mut i32) -> f64; + pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; + + cfg_if::cfg_if! { + if #[cfg(not(all(target_os = "windows", target_env = "msvc", target_arch = "x86")))] { + pub fn acosf(n: f32) -> f32; + pub fn asinf(n: f32) -> f32; + pub fn atan2f(a: f32, b: f32) -> f32; + pub fn atanf(n: f32) -> f32; + pub fn coshf(n: f32) -> f32; + pub fn sinhf(n: f32) -> f32; + pub fn tanf(n: f32) -> f32; + pub fn tanhf(n: f32) -> f32; + }} +} + +// On 32-bit x86 MSVC these functions aren't defined, so we just define shims +// which promote everything to f64, perform the calculation, and then demote +// back to f32. While not precisely correct should be "correct enough" for now. +cfg_if::cfg_if! { +if #[cfg(all(target_os = "windows", target_env = "msvc", target_arch = "x86"))] { + #[inline] + pub unsafe fn acosf(n: f32) -> f32 { + f64::acos(n as f64) as f32 + } + + #[inline] + pub unsafe fn asinf(n: f32) -> f32 { + f64::asin(n as f64) as f32 + } + + #[inline] + pub unsafe fn atan2f(n: f32, b: f32) -> f32 { + f64::atan2(n as f64, b as f64) as f32 + } + + #[inline] + pub unsafe fn atanf(n: f32) -> f32 { + f64::atan(n as f64) as f32 + } + + #[inline] + pub unsafe fn coshf(n: f32) -> f32 { + f64::cosh(n as f64) as f32 + } + + #[inline] + pub unsafe fn sinhf(n: f32) -> f32 { + f64::sinh(n as f64) as f32 + } + + #[inline] + pub unsafe fn tanf(n: f32) -> f32 { + f64::tan(n as f64) as f32 + } + + #[inline] + pub unsafe fn tanhf(n: f32) -> f32 { + f64::tanh(n as f64) as f32 + } +}} diff --git a/library/std/src/sys/cmath/builtins.rs b/library/std/src/sys/cmath/builtins.rs deleted file mode 100644 index c680132efa4b..000000000000 --- a/library/std/src/sys/cmath/builtins.rs +++ /dev/null @@ -1,35 +0,0 @@ -// These symbols are all defined by `libm`, -// or by `compiler-builtins` on unsupported platforms. - -extern "C" { - pub fn acos(n: f64) -> f64; - pub fn acosf(n: f32) -> f32; - pub fn asin(n: f64) -> f64; - pub fn asinf(n: f32) -> f32; - pub fn atan(n: f64) -> f64; - pub fn atan2(a: f64, b: f64) -> f64; - pub fn atan2f(a: f32, b: f32) -> f32; - pub fn atanf(n: f32) -> f32; - pub fn cbrt(n: f64) -> f64; - pub fn cbrtf(n: f32) -> f32; - pub fn cosh(n: f64) -> f64; - pub fn coshf(n: f32) -> f32; - pub fn expm1(n: f64) -> f64; - pub fn expm1f(n: f32) -> f32; - pub fn fdim(a: f64, b: f64) -> f64; - pub fn fdimf(a: f32, b: f32) -> f32; - pub fn hypot(x: f64, y: f64) -> f64; - pub fn hypotf(x: f32, y: f32) -> f32; - pub fn log1p(n: f64) -> f64; - pub fn log1pf(n: f32) -> f32; - pub fn sinh(n: f64) -> f64; - pub fn sinhf(n: f32) -> f32; - pub fn tan(n: f64) -> f64; - pub fn tanf(n: f32) -> f32; - pub fn tanh(n: f64) -> f64; - pub fn tanhf(n: f32) -> f32; - pub fn tgamma(n: f64) -> f64; - pub fn tgammaf(n: f32) -> f32; - pub fn lgamma_r(n: f64, s: &mut i32) -> f64; - pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; -} diff --git a/library/std/src/sys/cmath/mod.rs b/library/std/src/sys/cmath/mod.rs deleted file mode 100644 index 79d5021dd8dc..000000000000 --- a/library/std/src/sys/cmath/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![cfg(not(test))] - -cfg_if::cfg_if! { - if #[cfg(target_os = "windows")] { - mod windows; - pub use windows::*; - } else { - mod builtins; - pub use builtins::*; - } -} diff --git a/library/std/src/sys/cmath/windows.rs b/library/std/src/sys/cmath/windows.rs deleted file mode 100644 index 712097f06ff3..000000000000 --- a/library/std/src/sys/cmath/windows.rs +++ /dev/null @@ -1,94 +0,0 @@ -use core::ffi::{c_double, c_float, c_int}; - -extern "C" { - pub fn acos(n: c_double) -> c_double; - pub fn asin(n: c_double) -> c_double; - pub fn atan(n: c_double) -> c_double; - pub fn atan2(a: c_double, b: c_double) -> c_double; - pub fn cbrt(n: c_double) -> c_double; - pub fn cbrtf(n: c_float) -> c_float; - pub fn cosh(n: c_double) -> c_double; - pub fn expm1(n: c_double) -> c_double; - pub fn expm1f(n: c_float) -> c_float; - pub fn fdim(a: c_double, b: c_double) -> c_double; - pub fn fdimf(a: c_float, b: c_float) -> c_float; - #[cfg_attr(target_env = "msvc", link_name = "_hypot")] - pub fn hypot(x: c_double, y: c_double) -> c_double; - #[cfg_attr(target_env = "msvc", link_name = "_hypotf")] - pub fn hypotf(x: c_float, y: c_float) -> c_float; - pub fn log1p(n: c_double) -> c_double; - pub fn log1pf(n: c_float) -> c_float; - pub fn sinh(n: c_double) -> c_double; - pub fn tan(n: c_double) -> c_double; - pub fn tanh(n: c_double) -> c_double; - pub fn tgamma(n: c_double) -> c_double; - pub fn tgammaf(n: c_float) -> c_float; - pub fn lgamma_r(n: c_double, s: &mut c_int) -> c_double; - pub fn lgammaf_r(n: c_float, s: &mut c_int) -> c_float; -} - -pub use self::shims::*; - -#[cfg(not(all(target_env = "msvc", target_arch = "x86")))] -mod shims { - use core::ffi::c_float; - - extern "C" { - pub fn acosf(n: c_float) -> c_float; - pub fn asinf(n: c_float) -> c_float; - pub fn atan2f(a: c_float, b: c_float) -> c_float; - pub fn atanf(n: c_float) -> c_float; - pub fn coshf(n: c_float) -> c_float; - pub fn sinhf(n: c_float) -> c_float; - pub fn tanf(n: c_float) -> c_float; - pub fn tanhf(n: c_float) -> c_float; - } -} - -// On 32-bit x86 MSVC these functions aren't defined, so we just define shims -// which promote everything to f64, perform the calculation, and then demote -// back to f32. While not precisely correct should be "correct enough" for now. -#[cfg(all(target_env = "msvc", target_arch = "x86"))] -mod shims { - use core::ffi::c_float; - - #[inline] - pub unsafe fn acosf(n: c_float) -> c_float { - f64::acos(n as f64) as c_float - } - - #[inline] - pub unsafe fn asinf(n: c_float) -> c_float { - f64::asin(n as f64) as c_float - } - - #[inline] - pub unsafe fn atan2f(n: c_float, b: c_float) -> c_float { - f64::atan2(n as f64, b as f64) as c_float - } - - #[inline] - pub unsafe fn atanf(n: c_float) -> c_float { - f64::atan(n as f64) as c_float - } - - #[inline] - pub unsafe fn coshf(n: c_float) -> c_float { - f64::cosh(n as f64) as c_float - } - - #[inline] - pub unsafe fn sinhf(n: c_float) -> c_float { - f64::sinh(n as f64) as c_float - } - - #[inline] - pub unsafe fn tanf(n: c_float) -> c_float { - f64::tan(n as f64) as c_float - } - - #[inline] - pub unsafe fn tanhf(n: c_float) -> c_float { - f64::tanh(n as f64) as c_float - } -} diff --git a/library/std/src/sys/pal/unix/locks/mod.rs b/library/std/src/sys/pal/unix/locks/mod.rs index b2e0e49ad736..a49247310b54 100644 --- a/library/std/src/sys/pal/unix/locks/mod.rs +++ b/library/std/src/sys/pal/unix/locks/mod.rs @@ -22,10 +22,10 @@ cfg_if::cfg_if! { pub(crate) use futex_condvar::Condvar; } else { mod pthread_mutex; - mod pthread_rwlock; mod pthread_condvar; + mod queue_rwlock; pub(crate) use pthread_mutex::Mutex; - pub(crate) use pthread_rwlock::RwLock; + pub(crate) use queue_rwlock::RwLock; pub(crate) use pthread_condvar::Condvar; } } diff --git a/library/std/src/sys/pal/unix/locks/pthread_rwlock.rs b/library/std/src/sys/pal/unix/locks/pthread_rwlock.rs deleted file mode 100644 index 04662be9d827..000000000000 --- a/library/std/src/sys/pal/unix/locks/pthread_rwlock.rs +++ /dev/null @@ -1,195 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::mem::forget; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; - -struct AllocatedRwLock { - inner: UnsafeCell, - write_locked: UnsafeCell, // guarded by the `inner` RwLock - num_readers: AtomicUsize, -} - -unsafe impl Send for AllocatedRwLock {} -unsafe impl Sync for AllocatedRwLock {} - -pub struct RwLock { - inner: LazyBox, -} - -impl LazyInit for AllocatedRwLock { - fn init() -> Box { - Box::new(AllocatedRwLock { - inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER), - write_locked: UnsafeCell::new(false), - num_readers: AtomicUsize::new(0), - }) - } - - fn destroy(mut rwlock: Box) { - // We're not allowed to pthread_rwlock_destroy a locked rwlock, - // so check first if it's unlocked. - if *rwlock.write_locked.get_mut() || *rwlock.num_readers.get_mut() != 0 { - // The rwlock is locked. This happens if a RwLock{Read,Write}Guard is leaked. - // In this case, we just leak the RwLock too. - forget(rwlock); - } - } - - fn cancel_init(_: Box) { - // In this case, we can just drop it without any checks, - // since it cannot have been locked yet. - } -} - -impl AllocatedRwLock { - #[inline] - unsafe fn raw_unlock(&self) { - let r = libc::pthread_rwlock_unlock(self.inner.get()); - debug_assert_eq!(r, 0); - } -} - -impl Drop for AllocatedRwLock { - fn drop(&mut self) { - let r = unsafe { libc::pthread_rwlock_destroy(self.inner.get()) }; - // On DragonFly pthread_rwlock_destroy() returns EINVAL if called on a - // rwlock that was just initialized with - // libc::PTHREAD_RWLOCK_INITIALIZER. Once it is used (locked/unlocked) - // or pthread_rwlock_init() is called, this behaviour no longer occurs. - if cfg!(target_os = "dragonfly") { - debug_assert!(r == 0 || r == libc::EINVAL); - } else { - debug_assert_eq!(r, 0); - } - } -} - -impl RwLock { - #[inline] - pub const fn new() -> RwLock { - RwLock { inner: LazyBox::new() } - } - - #[inline] - pub fn read(&self) { - let lock = &*self.inner; - let r = unsafe { libc::pthread_rwlock_rdlock(lock.inner.get()) }; - - // According to POSIX, when a thread tries to acquire this read lock - // while it already holds the write lock - // (or vice versa, or tries to acquire the write lock twice), - // "the call shall either deadlock or return [EDEADLK]" - // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html, - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html). - // So, in principle, all we have to do here is check `r == 0` to be sure we properly - // got the lock. - // - // However, (at least) glibc before version 2.25 does not conform to this spec, - // and can return `r == 0` even when this thread already holds the write lock. - // We thus check for this situation ourselves and panic when detecting that a thread - // got the write lock more than once, or got a read and a write lock. - if r == libc::EAGAIN { - panic!("rwlock maximum reader count exceeded"); - } else if r == libc::EDEADLK || (r == 0 && unsafe { *lock.write_locked.get() }) { - // Above, we make sure to only access `write_locked` when `r == 0` to avoid - // data races. - if r == 0 { - // `pthread_rwlock_rdlock` succeeded when it should not have. - unsafe { - lock.raw_unlock(); - } - } - panic!("rwlock read lock would result in deadlock"); - } else { - // POSIX does not make guarantees about all the errors that may be returned. - // See issue #94705 for more details. - assert_eq!(r, 0, "unexpected error during rwlock read lock: {:?}", r); - lock.num_readers.fetch_add(1, Ordering::Relaxed); - } - } - - #[inline] - pub fn try_read(&self) -> bool { - let lock = &*self.inner; - let r = unsafe { libc::pthread_rwlock_tryrdlock(lock.inner.get()) }; - if r == 0 { - if unsafe { *lock.write_locked.get() } { - // `pthread_rwlock_tryrdlock` succeeded when it should not have. - unsafe { - lock.raw_unlock(); - } - false - } else { - lock.num_readers.fetch_add(1, Ordering::Relaxed); - true - } - } else { - false - } - } - - #[inline] - pub fn write(&self) { - let lock = &*self.inner; - let r = unsafe { libc::pthread_rwlock_wrlock(lock.inner.get()) }; - // See comments above for why we check for EDEADLK and write_locked. For the same reason, - // we also need to check that there are no readers (tracked in `num_readers`). - if r == libc::EDEADLK - || (r == 0 && unsafe { *lock.write_locked.get() }) - || lock.num_readers.load(Ordering::Relaxed) != 0 - { - // Above, we make sure to only access `write_locked` when `r == 0` to avoid - // data races. - if r == 0 { - // `pthread_rwlock_wrlock` succeeded when it should not have. - unsafe { - lock.raw_unlock(); - } - } - panic!("rwlock write lock would result in deadlock"); - } else { - // According to POSIX, for a properly initialized rwlock this can only - // return EDEADLK or 0. We rely on that. - debug_assert_eq!(r, 0); - } - - unsafe { - *lock.write_locked.get() = true; - } - } - - #[inline] - pub unsafe fn try_write(&self) -> bool { - let lock = &*self.inner; - let r = libc::pthread_rwlock_trywrlock(lock.inner.get()); - if r == 0 { - if *lock.write_locked.get() || lock.num_readers.load(Ordering::Relaxed) != 0 { - // `pthread_rwlock_trywrlock` succeeded when it should not have. - lock.raw_unlock(); - false - } else { - *lock.write_locked.get() = true; - true - } - } else { - false - } - } - - #[inline] - pub unsafe fn read_unlock(&self) { - let lock = &*self.inner; - debug_assert!(!*lock.write_locked.get()); - lock.num_readers.fetch_sub(1, Ordering::Relaxed); - lock.raw_unlock(); - } - - #[inline] - pub unsafe fn write_unlock(&self) { - let lock = &*self.inner; - debug_assert_eq!(lock.num_readers.load(Ordering::Relaxed), 0); - debug_assert!(*lock.write_locked.get()); - *lock.write_locked.get() = false; - lock.raw_unlock(); - } -} diff --git a/library/std/src/sys/pal/unix/locks/queue_rwlock.rs b/library/std/src/sys/pal/unix/locks/queue_rwlock.rs new file mode 100644 index 000000000000..0f02a98dfdd4 --- /dev/null +++ b/library/std/src/sys/pal/unix/locks/queue_rwlock.rs @@ -0,0 +1,557 @@ +//! Efficient read-write locking without `pthread_rwlock_t`. +//! +//! The readers-writer lock provided by the `pthread` library has a number of +//! problems which make it a suboptimal choice for `std`: +//! +//! * It is non-movable, so it needs to be allocated (lazily, to make the +//! constructor `const`). +//! * `pthread` is an external library, meaning the fast path of acquiring an +//! uncontended lock cannot be inlined. +//! * Some platforms (at least glibc before version 2.25) have buggy implementations +//! that can easily lead to undefined behaviour in safe Rust code when not properly +//! guarded against. +//! * On some platforms (e.g. macOS), the lock is very slow. +//! +//! Therefore, we implement our own `RwLock`! Naively, one might reach for a +//! spinlock, but those [can be quite problematic] when the lock is contended. +//! Instead, this readers-writer lock copies its implementation strategy from +//! the Windows [SRWLOCK] and the [usync] library. Spinning is still used for the +//! fast path, but it is bounded: after spinning fails, threads will locklessly +//! add an information structure containing a [`Thread`] handle into a queue of +//! waiters associated with the lock. The lock owner, upon releasing the lock, +//! will scan through the queue and wake up threads as appropriate, which will +//! then again try to acquire the lock. The resulting [`RwLock`] is: +//! +//! * adaptive, since it spins before doing any heavywheight parking operations +//! * allocation-free, modulo the per-thread [`Thread`] handle, which is +//! allocated regardless when using threads created by `std` +//! * writer-preferring, even if some readers may still slip through +//! * unfair, which reduces context-switching and thus drastically improves +//! performance +//! +//! and also quite fast in most cases. +//! +//! [can be quite problematic]: https://matklad.github.io/2020/01/02/spinlocks-considered-harmful.html +//! [SRWLOCK]: https://learn.microsoft.com/en-us/windows/win32/sync/slim-reader-writer--srw--locks +//! [usync]: https://crates.io/crates/usync +//! +//! # Implementation +//! +//! ## State +//! +//! A single [`AtomicPtr`] is used as state variable. The lowest three bits are used +//! to indicate the meaning of the remaining bits: +//! +//! | [`LOCKED`] | [`QUEUED`] | [`QUEUE_LOCKED`] | Remaining | | +//! |:-----------|:-----------|:-----------------|:-------------|:----------------------------------------------------------------------------------------------------------------------------| +//! | 0 | 0 | 0 | 0 | The lock is unlocked, no threads are waiting | +//! | 1 | 0 | 0 | 0 | The lock is write-locked, no threads waiting | +//! | 1 | 0 | 0 | n > 0 | The lock is read-locked with n readers | +//! | 0 | 1 | * | `*mut Node` | The lock is unlocked, but some threads are waiting. Only writers may lock the lock | +//! | 1 | 1 | * | `*mut Node` | The lock is locked, but some threads are waiting. If the lock is read-locked, the last queue node contains the reader count | +//! +//! ## Waiter queue +//! +//! When threads are waiting on the lock (`QUEUE` is set), the lock state +//! points to a queue of waiters, which is implemented as a linked list of +//! nodes stored on the stack to avoid memory allocation. To enable lockless +//! enqueuing of new nodes to the queue, the linked list is single-linked upon +//! creation. Since when the lock is read-locked, the lock count is stored in +//! the last link of the queue, threads have to traverse the queue to find the +//! last element upon releasing the lock. To avoid having to traverse the whole +//! list again and again, a pointer to the found tail is cached in the (current) +//! first element of the queue. +//! +//! Also, while the lock is unfair for performance reasons, it is still best to +//! wake the tail node first, which requires backlinks to previous nodes to be +//! created. This is done at the same time as finding the tail, and thus a set +//! tail field indicates the remaining portion of the queue is initialized. +//! +//! TLDR: Here's a diagram of what the queue looks like: +//! +//! ```text +//! state +//! │ +//! ▼ +//! ╭───────╮ next ╭───────╮ next ╭───────╮ next ╭───────╮ +//! │ ├─────►│ ├─────►│ ├─────►│ count │ +//! │ │ │ │ │ │ │ │ +//! │ │ │ │◄─────┤ │◄─────┤ │ +//! ╰───────╯ ╰───────╯ prev ╰───────╯ prev ╰───────╯ +//! │ ▲ +//! └───────────────────────────┘ +//! tail +//! ``` +//! +//! Invariants: +//! 1. At least one node must contain a non-null, current `tail` field. +//! 2. The first non-null `tail` field must be valid and current. +//! 3. All nodes preceding this node must have a correct, non-null `next` field. +//! 4. All nodes following this node must have a correct, non-null `prev` field. +//! +//! Access to the queue is controlled by the `QUEUE_LOCKED` bit, which threads +//! try to set both after enqueuing themselves to eagerly add backlinks to the +//! queue, which drastically improves performance, and after unlocking the lock +//! to wake the next waiter(s). This is done atomically at the same time as the +//! enqueuing/unlocking operation. The thread releasing the `QUEUE_LOCK` bit +//! will check the state of the lock and wake up waiters as appropriate. This +//! guarantees forward-progress even if the unlocking thread could not acquire +//! the queue lock. +//! +//! ## Memory orderings +//! +//! To properly synchronize changes to the data protected by the lock, the lock +//! is acquired and released with [`Acquire`] and [`Release`] ordering, respectively. +//! To propagate the initialization of nodes, changes to the queue lock are also +//! performed using these orderings. + +#![forbid(unsafe_op_in_unsafe_fn)] + +use crate::cell::OnceCell; +use crate::hint::spin_loop; +use crate::mem; +use crate::ptr::{self, invalid_mut, null_mut, NonNull}; +use crate::sync::atomic::{ + AtomicBool, AtomicPtr, + Ordering::{AcqRel, Acquire, Relaxed, Release}, +}; +use crate::sys_common::thread_info; +use crate::thread::Thread; + +// Locking uses exponential backoff. `SPIN_COUNT` indicates how many times the +// locking operation will be retried. +// `spin_loop` will be called `2.pow(SPIN_COUNT) - 1` times. +const SPIN_COUNT: usize = 7; + +type State = *mut (); +type AtomicState = AtomicPtr<()>; + +const UNLOCKED: State = invalid_mut(0); +const LOCKED: usize = 1; +const QUEUED: usize = 2; +const QUEUE_LOCKED: usize = 4; +const SINGLE: usize = 8; +const MASK: usize = !(QUEUE_LOCKED | QUEUED | LOCKED); + +/// Marks the state as write-locked, if possible. +#[inline] +fn write_lock(state: State) -> Option { + let state = state.wrapping_byte_add(LOCKED); + if state.addr() & LOCKED == LOCKED { Some(state) } else { None } +} + +/// Marks the state as read-locked, if possible. +#[inline] +fn read_lock(state: State) -> Option { + if state.addr() & QUEUED == 0 && state.addr() != LOCKED { + Some(invalid_mut(state.addr().checked_add(SINGLE)? | LOCKED)) + } else { + None + } +} + +/// Masks the state, assuming it points to a queue node. +/// +/// # Safety +/// The state must contain a valid pointer to a queue node. +#[inline] +unsafe fn to_node(state: State) -> NonNull { + unsafe { NonNull::new_unchecked(state.mask(MASK)).cast() } +} + +/// An atomic node pointer with relaxed operations. +struct AtomicLink(AtomicPtr); + +impl AtomicLink { + fn new(v: Option>) -> AtomicLink { + AtomicLink(AtomicPtr::new(v.map_or(null_mut(), NonNull::as_ptr))) + } + + fn get(&self) -> Option> { + NonNull::new(self.0.load(Relaxed)) + } + + fn set(&self, v: Option>) { + self.0.store(v.map_or(null_mut(), NonNull::as_ptr), Relaxed); + } +} + +#[repr(align(8))] +struct Node { + next: AtomicLink, + prev: AtomicLink, + tail: AtomicLink, + write: bool, + thread: OnceCell, + completed: AtomicBool, +} + +impl Node { + /// Create a new queue node. + fn new(write: bool) -> Node { + Node { + next: AtomicLink::new(None), + prev: AtomicLink::new(None), + tail: AtomicLink::new(None), + write, + thread: OnceCell::new(), + completed: AtomicBool::new(false), + } + } + + /// Prepare this node for waiting. + fn prepare(&mut self) { + // Fall back to creating an unnamed `Thread` handle to allow locking in + // TLS destructors. + self.thread + .get_or_init(|| thread_info::current_thread().unwrap_or_else(|| Thread::new(None))); + self.completed = AtomicBool::new(false); + } + + /// Wait until this node is marked as completed. + /// + /// # Safety + /// May only be called from the thread that created the node. + unsafe fn wait(&self) { + while !self.completed.load(Acquire) { + unsafe { + self.thread.get().unwrap().park(); + } + } + } + + /// Atomically mark this node as completed. The node may not outlive this call. + unsafe fn complete(this: NonNull) { + // Since the node may be destroyed immediately after the completed flag + // is set, clone the thread handle before that. + let thread = unsafe { this.as_ref().thread.get().unwrap().clone() }; + unsafe { + this.as_ref().completed.store(true, Release); + } + thread.unpark(); + } +} + +struct PanicGuard; + +impl Drop for PanicGuard { + fn drop(&mut self) { + rtabort!("tried to drop node in intrusive list."); + } +} + +/// Add backlinks to the queue, returning the tail. +/// +/// May be called from multiple threads at the same time, while the queue is not +/// modified (this happens when unlocking multiple readers). +/// +/// # Safety +/// * `head` must point to a node in a valid queue. +/// * `head` must be or be in front of the head of the queue at the time of the +/// last removal. +/// * The part of the queue starting with `head` must not be modified during this +/// call. +unsafe fn add_backlinks_and_find_tail(head: NonNull) -> NonNull { + let mut current = head; + let tail = loop { + let c = unsafe { current.as_ref() }; + match c.tail.get() { + Some(tail) => break tail, + // SAFETY: + // All `next` fields before the first node with a `set` tail are + // non-null and valid (invariant 3). + None => unsafe { + let next = c.next.get().unwrap_unchecked(); + next.as_ref().prev.set(Some(current)); + current = next; + }, + } + }; + + unsafe { + head.as_ref().tail.set(Some(tail)); + tail + } +} + +pub struct RwLock { + state: AtomicState, +} + +impl RwLock { + #[inline] + pub const fn new() -> RwLock { + RwLock { state: AtomicPtr::new(UNLOCKED) } + } + + #[inline] + pub fn try_read(&self) -> bool { + self.state.fetch_update(Acquire, Relaxed, read_lock).is_ok() + } + + #[inline] + pub fn read(&self) { + if !self.try_read() { + self.lock_contended(false) + } + } + + #[inline] + pub fn try_write(&self) -> bool { + // Atomically set the `LOCKED` bit. This is lowered to a single atomic + // instruction on most modern processors (e.g. "lock bts" on x86 and + // "ldseta" on modern AArch64), and therefore is more efficient than + // `fetch_update(lock(true))`, which can spuriously fail if a new node + // is appended to the queue. + self.state.fetch_or(LOCKED, Acquire).addr() & LOCKED == 0 + } + + #[inline] + pub fn write(&self) { + if !self.try_write() { + self.lock_contended(true) + } + } + + #[cold] + fn lock_contended(&self, write: bool) { + let update = if write { write_lock } else { read_lock }; + let mut node = Node::new(write); + let mut state = self.state.load(Relaxed); + let mut count = 0; + loop { + if let Some(next) = update(state) { + // The lock is available, try locking it. + match self.state.compare_exchange_weak(state, next, Acquire, Relaxed) { + Ok(_) => return, + Err(new) => state = new, + } + } else if state.addr() & QUEUED == 0 && count < SPIN_COUNT { + // If the lock is not available and no threads are queued, spin + // for a while, using exponential backoff to decrease cache + // contention. + for _ in 0..(1 << count) { + spin_loop(); + } + state = self.state.load(Relaxed); + count += 1; + } else { + // Fall back to parking. First, prepare the node. + node.prepare(); + + // If there are threads queued, set the `next` field to a + // pointer to the next node in the queue. Otherwise set it to + // the lock count if the state is read-locked or to zero if it + // is write-locked. + node.next.0 = AtomicPtr::new(state.mask(MASK).cast()); + node.prev = AtomicLink::new(None); + let mut next = ptr::from_ref(&node) + .map_addr(|addr| addr | QUEUED | (state.addr() & LOCKED)) + as State; + + if state.addr() & QUEUED == 0 { + // If this is the first node in the queue, set the tail field to + // the node itself to ensure there is a current `tail` field in + // the queue (invariants 1 and 2). This needs to use `set` to + // avoid invalidating the new pointer. + node.tail.set(Some(NonNull::from(&node))); + } else { + // Otherwise, the tail of the queue is not known. + node.tail.set(None); + // Try locking the queue to eagerly add backlinks. + next = next.map_addr(|addr| addr | QUEUE_LOCKED); + } + + // Register the node, using release ordering to propagate our + // changes to the waking thread. + if let Err(new) = self.state.compare_exchange_weak(state, next, AcqRel, Relaxed) { + // The state has changed, just try again. + state = new; + continue; + } + + // The node is registered, so the structure must not be + // mutably accessed or destroyed while other threads may + // be accessing it. Guard against unwinds using a panic + // guard that aborts when dropped. + let guard = PanicGuard; + + // If the current thread locked the queue, unlock it again, + // linking it in the process. + if state.addr() & (QUEUE_LOCKED | QUEUED) == QUEUED { + unsafe { + self.unlock_queue(next); + } + } + + // Wait until the node is removed from the queue. + // SAFETY: the node was created by the current thread. + unsafe { + node.wait(); + } + + // The node was removed from the queue, disarm the guard. + mem::forget(guard); + + // Reload the state and try again. + state = self.state.load(Relaxed); + count = 0; + } + } + } + + #[inline] + pub unsafe fn read_unlock(&self) { + match self.state.fetch_update(Release, Acquire, |state| { + if state.addr() & QUEUED == 0 { + let count = state.addr() - (SINGLE | LOCKED); + Some(if count > 0 { invalid_mut(count | LOCKED) } else { UNLOCKED }) + } else { + None + } + }) { + Ok(_) => {} + // There are waiters queued and the lock count was moved to the + // tail of the queue. + Err(state) => unsafe { self.read_unlock_contended(state) }, + } + } + + #[cold] + unsafe fn read_unlock_contended(&self, state: State) { + // The state was observed with acquire ordering above, so the current + // thread will observe all node initializations. + + // SAFETY: + // Because new read-locks cannot be acquired while threads are queued, + // all queue-lock owners will observe the set `LOCKED` bit. Because they + // do not modify the queue while there is a lock owner, the queue will + // not be removed from here. + let tail = unsafe { add_backlinks_and_find_tail(to_node(state)).as_ref() }; + // The lock count is stored in the `next` field of `tail`. + // Decrement it, making sure to observe all changes made to the queue + // by the other lock owners by using acquire-release ordering. + let was_last = tail.next.0.fetch_byte_sub(SINGLE, AcqRel).addr() - SINGLE == 0; + if was_last { + // SAFETY: + // Other threads cannot read-lock while threads are queued. Also, + // the `LOCKED` bit is still set, so there are no writers. Therefore, + // the current thread exclusively owns the lock. + unsafe { self.unlock_contended(state) } + } + } + + #[inline] + pub unsafe fn write_unlock(&self) { + if let Err(state) = + self.state.compare_exchange(invalid_mut(LOCKED), UNLOCKED, Release, Relaxed) + { + // SAFETY: + // Since other threads cannot acquire the lock, the state can only + // have changed because there are threads queued on the lock. + unsafe { self.unlock_contended(state) } + } + } + + /// # Safety + /// * The lock must be exclusively owned by this thread. + /// * There must be threads queued on the lock. + #[cold] + unsafe fn unlock_contended(&self, mut state: State) { + loop { + // Atomically release the lock and try to acquire the queue lock. + let next = state.map_addr(|a| (a & !LOCKED) | QUEUE_LOCKED); + match self.state.compare_exchange_weak(state, next, AcqRel, Relaxed) { + // The queue lock was acquired. Release it, waking up the next + // waiter in the process. + Ok(_) if state.addr() & QUEUE_LOCKED == 0 => unsafe { + return self.unlock_queue(next); + }, + // Another thread already holds the queue lock, leave waking up + // waiters to it. + Ok(_) => return, + Err(new) => state = new, + } + } + } + + /// Unlocks the queue. If the lock is unlocked, wakes up the next eligible + /// thread(s). + /// + /// # Safety + /// The queue lock must be held by the current thread. + unsafe fn unlock_queue(&self, mut state: State) { + debug_assert_eq!(state.addr() & (QUEUED | QUEUE_LOCKED), QUEUED | QUEUE_LOCKED); + + loop { + let tail = unsafe { add_backlinks_and_find_tail(to_node(state)) }; + + if state.addr() & LOCKED == LOCKED { + // Another thread has locked the lock. Leave waking up waiters + // to them by releasing the queue lock. + match self.state.compare_exchange_weak( + state, + state.mask(!QUEUE_LOCKED), + Release, + Acquire, + ) { + Ok(_) => return, + Err(new) => { + state = new; + continue; + } + } + } + + let is_writer = unsafe { tail.as_ref().write }; + if is_writer && let Some(prev) = unsafe { tail.as_ref().prev.get() } { + // `tail` is a writer and there is a node before `tail`. + // Split off `tail`. + + // There are no set `tail` links before the node pointed to by + // `state`, so the first non-null tail field will be current + // (invariant 2). Invariant 4 is fullfilled since `find_tail` + // was called on this node, which ensures all backlinks are set. + unsafe { + to_node(state).as_ref().tail.set(Some(prev)); + } + + // Release the queue lock. Doing this by subtraction is more + // efficient on modern processors since it is a single instruction + // instead of an update loop, which will fail if new threads are + // added to the list. + self.state.fetch_byte_sub(QUEUE_LOCKED, Release); + + // The tail was split off and the lock released. Mark the node as + // completed. + unsafe { + return Node::complete(tail); + } + } else { + // The next waiter is a reader or the queue only consists of one + // waiter. Just wake all threads. + + // The lock cannot be locked (checked above), so mark it as + // unlocked to reset the queue. + if let Err(new) = + self.state.compare_exchange_weak(state, UNLOCKED, Release, Acquire) + { + state = new; + continue; + } + + let mut current = tail; + loop { + let prev = unsafe { current.as_ref().prev.get() }; + unsafe { + Node::complete(current); + } + match prev { + Some(prev) => current = prev, + None => return, + } + } + } + } + } +} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 8498937809e7..eb837c8f6c63 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -1063,7 +1063,7 @@ pub fn park() { let guard = PanicGuard; // SAFETY: park_timeout is called on the parker owned by this thread. unsafe { - current().inner.as_ref().parker().park(); + current().park(); } // No panic occurred, do not abort. forget(guard); @@ -1290,6 +1290,15 @@ impl Thread { Thread { inner } } + /// Like the public [`park`], but callable on any handle. This is used to + /// allow parking in TLS destructors. + /// + /// # Safety + /// May only be called from the thread to which this handle belongs. + pub(crate) unsafe fn park(&self) { + unsafe { self.inner.as_ref().parker().park() } + } + /// Atomically makes the handle's token available if it is not already. /// /// Every thread is equipped with some basic low-level blocking support, via diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 5f0afdb1b36c..4a04dbf44aa6 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -4,7 +4,9 @@ use crate::core::build_steps::compile::{ add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, }; use crate::core::build_steps::tool::{prepare_tool_cargo, SourceType}; -use crate::core::builder::{crate_description, Alias, Builder, Kind, RunConfig, ShouldRun, Step}; +use crate::core::builder::{ + self, crate_description, Alias, Builder, Kind, RunConfig, ShouldRun, Step, +}; use crate::core::config::TargetSelection; use crate::utils::cache::Interned; use crate::INTERNER; @@ -110,13 +112,15 @@ impl Step for Std { let target = self.target; let compiler = builder.compiler(builder.top_stage, builder.config.build); - let mut cargo = builder.cargo( + let mut cargo = builder::Cargo::new( + builder, compiler, Mode::Std, SourceType::InTree, target, cargo_subcommand(builder.kind), ); + std_cargo(builder, target, compiler.stage, &mut cargo); if matches!(builder.config.cmd, Subcommand::Fix { .. }) { // By default, cargo tries to fix all targets. Tell it not to fix tests until we've added `test` to the sysroot. @@ -162,7 +166,8 @@ impl Step for Std { // since we initialize with an empty sysroot. // // Currently only the "libtest" tree of crates does this. - let mut cargo = builder.cargo( + let mut cargo = builder::Cargo::new( + builder, compiler, Mode::Std, SourceType::InTree, @@ -256,13 +261,15 @@ impl Step for Rustc { builder.ensure(Std::new(target)); } - let mut cargo = builder.cargo( + let mut cargo = builder::Cargo::new( + builder, compiler, Mode::Rustc, SourceType::InTree, target, cargo_subcommand(builder.kind), ); + rustc_cargo(builder, &mut cargo, target, compiler.stage); // For ./x.py clippy, don't run with --all-targets because @@ -332,13 +339,15 @@ impl Step for CodegenBackend { builder.ensure(Rustc::new(target, builder)); - let mut cargo = builder.cargo( + let mut cargo = builder::Cargo::new( + builder, compiler, Mode::Codegen, SourceType::InTree, target, cargo_subcommand(builder.kind), ); + cargo .arg("--manifest-path") .arg(builder.src.join(format!("compiler/rustc_codegen_{backend}/Cargo.toml"))); diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 27b1d311864f..64bef2d30158 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -22,6 +22,7 @@ use serde_derive::Deserialize; use crate::core::build_steps::dist; use crate::core::build_steps::llvm; use crate::core::build_steps::tool::SourceType; +use crate::core::builder; use crate::core::builder::crate_description; use crate::core::builder::Cargo; use crate::core::builder::{Builder, Kind, PathSet, RunConfig, ShouldRun, Step, TaskPath}; @@ -239,12 +240,26 @@ impl Step for Std { // with -Zalways-encode-mir. This frees us from the need to have a target linker, and the // fact that this is a check build integrates nicely with run_cargo. let mut cargo = if self.is_for_mir_opt_tests { - let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "check"); + let mut cargo = builder::Cargo::new_for_mir_opt_tests( + builder, + compiler, + Mode::Std, + SourceType::InTree, + target, + "check", + ); cargo.rustflag("-Zalways-encode-mir"); cargo.arg("--manifest-path").arg(builder.src.join("library/sysroot/Cargo.toml")); cargo } else { - let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "build"); + let mut cargo = builder::Cargo::new( + builder, + compiler, + Mode::Std, + SourceType::InTree, + target, + "build", + ); std_cargo(builder, target, compiler.stage, &mut cargo); for krate in &*self.crates { cargo.arg("-p").arg(krate); @@ -913,7 +928,15 @@ impl Step for Rustc { builder.config.build, )); - let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "build"); + let mut cargo = builder::Cargo::new( + builder, + compiler, + Mode::Rustc, + SourceType::InTree, + target, + "build", + ); + rustc_cargo(builder, &mut cargo, target, compiler.stage); if builder.config.rust_profile_use.is_some() @@ -1333,7 +1356,14 @@ impl Step for CodegenBackend { let out_dir = builder.cargo_out(compiler, Mode::Codegen, target); - let mut cargo = builder.cargo(compiler, Mode::Codegen, SourceType::InTree, target, "build"); + let mut cargo = builder::Cargo::new( + builder, + compiler, + Mode::Codegen, + SourceType::InTree, + target, + "build", + ); cargo .arg("--manifest-path") .arg(builder.src.join(format!("compiler/rustc_codegen_{backend}/Cargo.toml"))); diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 57e63927c95e..fdb099e4ab1d 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -13,7 +13,7 @@ use std::{fs, mem}; use crate::core::build_steps::compile; use crate::core::build_steps::tool::{self, prepare_tool_cargo, SourceType, Tool}; -use crate::core::builder::crate_description; +use crate::core::builder::{self, crate_description}; use crate::core::builder::{Alias, Builder, Compiler, Kind, RunConfig, ShouldRun, Step}; use crate::core::config::{Config, TargetSelection}; use crate::utils::cache::{Interned, INTERNER}; @@ -684,7 +684,9 @@ fn doc_std( // as a function parameter. let out_dir = target_dir.join(target.triple).join("doc"); - let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "doc"); + let mut cargo = + builder::Cargo::new(builder, compiler, Mode::Std, SourceType::InTree, target, "doc"); + compile::std_cargo(builder, target, compiler.stage, &mut cargo); cargo .arg("--no-deps") @@ -785,7 +787,9 @@ impl Step for Rustc { ); // Build cargo command. - let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "doc"); + let mut cargo = + builder::Cargo::new(builder, compiler, Mode::Rustc, SourceType::InTree, target, "doc"); + cargo.rustdocflag("--document-private-items"); // Since we always pass --document-private-items, there's no need to warn about linking to private items. cargo.rustdocflag("-Arustdoc::private-intra-doc-links"); diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index d9e0da14a70b..5fa5f2d47946 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -165,7 +165,7 @@ impl Step for Miri { SourceType::InTree, &[], ); - miri.add_rustc_lib_path(builder, compiler); + miri.add_rustc_lib_path(builder); // Forward arguments. miri.arg("--").arg("--target").arg(target.rustc_target_arg()); miri.args(builder.config.args()); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index baf1b5a4a1af..1dbda405128a 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -20,6 +20,7 @@ use crate::core::build_steps::llvm; use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget; use crate::core::build_steps::tool::{self, SourceType, Tool}; use crate::core::build_steps::toolstate::ToolState; +use crate::core::builder; use crate::core::builder::crate_description; use crate::core::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step}; use crate::core::config::flags::get_completion; @@ -380,7 +381,7 @@ impl Step for RustAnalyzer { // work in Rust CI cargo.env("SKIP_SLOW_TESTS", "1"); - cargo.add_rustc_lib_path(builder, compiler); + cargo.add_rustc_lib_path(builder); run_cargo_test(cargo, &[], &[], "rust-analyzer", "rust-analyzer", compiler, host, builder); } } @@ -426,7 +427,7 @@ impl Step for Rustfmt { t!(fs::create_dir_all(&dir)); cargo.env("RUSTFMT_TEST_DIR", dir); - cargo.add_rustc_lib_path(builder, compiler); + cargo.add_rustc_lib_path(builder); run_cargo_test(cargo, &[], &[], "rustfmt", "rustfmt", compiler, host, builder); } @@ -476,7 +477,7 @@ impl Step for RustDemangler { t!(fs::create_dir_all(&dir)); cargo.env("RUST_DEMANGLER_DRIVER_PATH", rust_demangler); - cargo.add_rustc_lib_path(builder, compiler); + cargo.add_rustc_lib_path(builder); run_cargo_test( cargo, @@ -517,7 +518,7 @@ impl Miri { SourceType::InTree, &[], ); - cargo.add_rustc_lib_path(builder, compiler); + cargo.add_rustc_lib_path(builder); cargo.arg("--").arg("miri").arg("setup"); cargo.arg("--target").arg(target.rustc_target_arg()); @@ -618,7 +619,7 @@ impl Step for Miri { ); let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, target); - cargo.add_rustc_lib_path(builder, compiler); + cargo.add_rustc_lib_path(builder); // miri tests need to know about the stage sysroot cargo.env("MIRI_SYSROOT", &miri_sysroot); @@ -671,7 +672,7 @@ impl Step for Miri { SourceType::Submodule, &[], ); - cargo.add_rustc_lib_path(builder, compiler); + cargo.add_rustc_lib_path(builder); cargo.arg("--").arg("miri").arg("test"); if builder.config.locked_deps { cargo.arg("--locked"); @@ -788,7 +789,7 @@ impl Step for Clippy { let host_libs = builder.stage_out(compiler, Mode::ToolRustc).join(builder.cargo_dir()); cargo.env("HOST_LIBS", host_libs); - cargo.add_rustc_lib_path(builder, compiler); + cargo.add_rustc_lib_path(builder); let mut cargo = prepare_cargo_test(cargo, &[], &[], "clippy", compiler, host, builder); let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "clippy", host, host); @@ -2499,8 +2500,15 @@ impl Step for Crate { // we're working with automatically. let compiler = builder.compiler_for(compiler.stage, compiler.host, target); - let mut cargo = - builder.cargo(compiler, mode, SourceType::InTree, target, builder.kind.as_str()); + let mut cargo = builder::Cargo::new( + builder, + compiler, + mode, + SourceType::InTree, + target, + builder.kind.as_str(), + ); + match mode { Mode::Std => { compile::std_cargo(builder, target, compiler.stage, &mut cargo); @@ -3134,13 +3142,15 @@ impl Step for CodegenCranelift { let compiler = builder.compiler_for(compiler.stage, compiler.host, target); let build_cargo = || { - let mut cargo = builder.cargo( + let mut cargo = builder::Cargo::new( + builder, compiler, Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works SourceType::InTree, target, "run", ); + cargo.current_dir(&builder.src.join("compiler/rustc_codegen_cranelift")); cargo .arg("--manifest-path") @@ -3260,13 +3270,15 @@ impl Step for CodegenGCC { let compiler = builder.compiler_for(compiler.stage, compiler.host, target); let build_cargo = || { - let mut cargo = builder.cargo( + let mut cargo = builder::Cargo::new( + builder, compiler, Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works SourceType::InTree, target, "run", ); + cargo.current_dir(&builder.src.join("compiler/rustc_codegen_gcc")); cargo .arg("--manifest-path") diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 5d8d10a7debc..a3daf22c9a99 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -5,6 +5,7 @@ use std::process::Command; use crate::core::build_steps::compile; use crate::core::build_steps::toolstate::ToolState; +use crate::core::builder; use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; use crate::utils::channel::GitInfo; @@ -142,7 +143,8 @@ pub fn prepare_tool_cargo( source_type: SourceType, extra_features: &[String], ) -> CargoCommand { - let mut cargo = builder.cargo(compiler, mode, source_type, target, command); + let mut cargo = builder::Cargo::new(builder, compiler, mode, source_type, target, command); + let dir = builder.src.join(path); cargo.arg("--manifest-path").arg(dir.join("Cargo.toml")); diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 83485abfa561..b21ffe868e14 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1299,14 +1299,12 @@ impl<'a> Builder<'a> { cargo } - /// Prepares an invocation of `cargo` to be run. - /// /// This will create a `Command` that represents a pending execution of /// Cargo. This cargo will be configured to use `compiler` as the actual /// rustc compiler, its output will be scoped by `mode`'s output directory, /// it will pass the `--target` flag for the specified `target`, and will be /// executing the Cargo command `cmd`. - pub fn cargo( + fn cargo( &self, compiler: Compiler, mode: Mode, @@ -1872,17 +1870,6 @@ impl<'a> Builder<'a> { rustflags.arg("-Wrustc::internal"); } - if cmd != "check" { - self.configure_linker( - compiler, - target, - &mut cargo, - &mut rustflags, - &mut rustdocflags, - &mut hostflags, - ); - } - // If Control Flow Guard is enabled, pass the `control-flow-guard` flag to rustc // when compiling the standard library, since this might be linked into the final outputs // produced by rustc. Since this mitigation is only available on Windows, only enable it @@ -2031,136 +2018,14 @@ impl<'a> Builder<'a> { cargo.env("RUSTFLAGS", &rustc_args.join(" ")); } - Cargo { command: cargo, rustflags, rustdocflags, hostflags, allow_features } - } - - fn configure_linker( - &self, - compiler: Compiler, - target: TargetSelection, - cargo: &mut Command, - rustflags: &mut Rustflags, - rustdocflags: &mut Rustflags, - hostflags: &mut HostFlags, - ) { - // Dealing with rpath here is a little special, so let's go into some - // detail. First off, `-rpath` is a linker option on Unix platforms - // which adds to the runtime dynamic loader path when looking for - // dynamic libraries. We use this by default on Unix platforms to ensure - // that our nightlies behave the same on Windows, that is they work out - // of the box. This can be disabled by setting `rpath = false` in `[rust]` - // table of `config.toml` - // - // Ok, so the astute might be wondering "why isn't `-C rpath` used - // here?" and that is indeed a good question to ask. This codegen - // option is the compiler's current interface to generating an rpath. - // Unfortunately it doesn't quite suffice for us. The flag currently - // takes no value as an argument, so the compiler calculates what it - // should pass to the linker as `-rpath`. This unfortunately is based on - // the **compile time** directory structure which when building with - // Cargo will be very different than the runtime directory structure. - // - // All that's a really long winded way of saying that if we use - // `-Crpath` then the executables generated have the wrong rpath of - // something like `$ORIGIN/deps` when in fact the way we distribute - // rustc requires the rpath to be `$ORIGIN/../lib`. - // - // So, all in all, to set up the correct rpath we pass the linker - // argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it - // fun to pass a flag to a tool to pass a flag to pass a flag to a tool - // to change a flag in a binary? - if self.config.rpath_enabled(target) && helpers::use_host_linker(target) { - let libdir = self.sysroot_libdir_relative(compiler).to_str().unwrap(); - let rpath = if target.contains("apple") { - // Note that we need to take one extra step on macOS to also pass - // `-Wl,-instal_name,@rpath/...` to get things to work right. To - // do that we pass a weird flag to the compiler to get it to do - // so. Note that this is definitely a hack, and we should likely - // flesh out rpath support more fully in the future. - rustflags.arg("-Zosx-rpath-install-name"); - Some(format!("-Wl,-rpath,@loader_path/../{libdir}")) - } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") { - rustflags.arg("-Clink-args=-Wl,-z,origin"); - Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}")) - } else { - None - }; - if let Some(rpath) = rpath { - rustflags.arg(&format!("-Clink-args={rpath}")); - } - } - - for arg in linker_args(self, compiler.host, LldThreads::Yes) { - hostflags.arg(&arg); - } - - if let Some(target_linker) = self.linker(target) { - let target = crate::envify(&target.triple); - cargo.env(&format!("CARGO_TARGET_{target}_LINKER"), target_linker); - } - // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not - // `linker_args` here. - for flag in linker_flags(self, target, LldThreads::Yes) { - rustflags.arg(&flag); - } - for arg in linker_args(self, target, LldThreads::Yes) { - rustdocflags.arg(&arg); - } - - if !self.config.dry_run() && self.cc.borrow()[&target].args().iter().any(|arg| arg == "-gz") - { - rustflags.arg("-Clink-arg=-gz"); - } - - // Throughout the build Cargo can execute a number of build scripts - // compiling C/C++ code and we need to pass compilers, archivers, flags, etc - // obtained previously to those build scripts. - // Build scripts use either the `cc` crate or `configure/make` so we pass - // the options through environment variables that are fetched and understood by both. - // - // FIXME: the guard against msvc shouldn't need to be here - if target.is_msvc() { - if let Some(ref cl) = self.config.llvm_clang_cl { - cargo.env("CC", cl).env("CXX", cl); - } - } else { - let ccache = self.config.ccache.as_ref(); - let ccacheify = |s: &Path| { - let ccache = match ccache { - Some(ref s) => s, - None => return s.display().to_string(), - }; - // FIXME: the cc-rs crate only recognizes the literal strings - // `ccache` and `sccache` when doing caching compilations, so we - // mirror that here. It should probably be fixed upstream to - // accept a new env var or otherwise work with custom ccache - // vars. - match &ccache[..] { - "ccache" | "sccache" => format!("{} {}", ccache, s.display()), - _ => s.display().to_string(), - } - }; - let triple_underscored = target.triple.replace("-", "_"); - let cc = ccacheify(&self.cc(target)); - cargo.env(format!("CC_{triple_underscored}"), &cc); - - let cflags = self.cflags(target, GitRepo::Rustc, CLang::C).join(" "); - cargo.env(format!("CFLAGS_{triple_underscored}"), &cflags); - - if let Some(ar) = self.ar(target) { - let ranlib = format!("{} s", ar.display()); - cargo - .env(format!("AR_{triple_underscored}"), ar) - .env(format!("RANLIB_{triple_underscored}"), ranlib); - } - - if let Ok(cxx) = self.cxx(target) { - let cxx = ccacheify(&cxx); - let cxxflags = self.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" "); - cargo - .env(format!("CXX_{triple_underscored}"), &cxx) - .env(format!("CXXFLAGS_{triple_underscored}"), cxxflags); - } + Cargo { + command: cargo, + compiler, + target, + rustflags, + rustdocflags, + hostflags, + allow_features, } } @@ -2363,6 +2228,8 @@ impl HostFlags { #[derive(Debug)] pub struct Cargo { command: Command, + compiler: Compiler, + target: TargetSelection, rustflags: Rustflags, rustdocflags: Rustflags, hostflags: HostFlags, @@ -2370,10 +2237,37 @@ pub struct Cargo { } impl Cargo { + /// Calls `Builder::cargo` and `Cargo::configure_linker` to prepare an invocation of `cargo` to be run. + pub fn new( + builder: &Builder<'_>, + compiler: Compiler, + mode: Mode, + source_type: SourceType, + target: TargetSelection, + cmd: &str, + ) -> Cargo { + let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd); + cargo.configure_linker(builder); + cargo + } + + /// Same as `Cargo::new` except this one doesn't configure the linker with `Cargo::configure_linker` + pub fn new_for_mir_opt_tests( + builder: &Builder<'_>, + compiler: Compiler, + mode: Mode, + source_type: SourceType, + target: TargetSelection, + cmd: &str, + ) -> Cargo { + builder.cargo(compiler, mode, source_type, target, cmd) + } + pub fn rustdocflag(&mut self, arg: &str) -> &mut Cargo { self.rustdocflags.arg(arg); self } + pub fn rustflag(&mut self, arg: &str) -> &mut Cargo { self.rustflags.arg(arg); self @@ -2403,8 +2297,8 @@ impl Cargo { self } - pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>, compiler: Compiler) { - builder.add_rustc_lib_path(compiler, &mut self.command); + pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>) { + builder.add_rustc_lib_path(self.compiler, &mut self.command); } pub fn current_dir(&mut self, dir: &Path) -> &mut Cargo { @@ -2423,6 +2317,134 @@ impl Cargo { self.allow_features.push_str(features); self } + + fn configure_linker(&mut self, builder: &Builder<'_>) -> &mut Cargo { + let target = self.target; + let compiler = self.compiler; + + // Dealing with rpath here is a little special, so let's go into some + // detail. First off, `-rpath` is a linker option on Unix platforms + // which adds to the runtime dynamic loader path when looking for + // dynamic libraries. We use this by default on Unix platforms to ensure + // that our nightlies behave the same on Windows, that is they work out + // of the box. This can be disabled by setting `rpath = false` in `[rust]` + // table of `config.toml` + // + // Ok, so the astute might be wondering "why isn't `-C rpath` used + // here?" and that is indeed a good question to ask. This codegen + // option is the compiler's current interface to generating an rpath. + // Unfortunately it doesn't quite suffice for us. The flag currently + // takes no value as an argument, so the compiler calculates what it + // should pass to the linker as `-rpath`. This unfortunately is based on + // the **compile time** directory structure which when building with + // Cargo will be very different than the runtime directory structure. + // + // All that's a really long winded way of saying that if we use + // `-Crpath` then the executables generated have the wrong rpath of + // something like `$ORIGIN/deps` when in fact the way we distribute + // rustc requires the rpath to be `$ORIGIN/../lib`. + // + // So, all in all, to set up the correct rpath we pass the linker + // argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it + // fun to pass a flag to a tool to pass a flag to pass a flag to a tool + // to change a flag in a binary? + if builder.config.rpath_enabled(target) && helpers::use_host_linker(target) { + let libdir = builder.sysroot_libdir_relative(compiler).to_str().unwrap(); + let rpath = if target.contains("apple") { + // Note that we need to take one extra step on macOS to also pass + // `-Wl,-instal_name,@rpath/...` to get things to work right. To + // do that we pass a weird flag to the compiler to get it to do + // so. Note that this is definitely a hack, and we should likely + // flesh out rpath support more fully in the future. + self.rustflags.arg("-Zosx-rpath-install-name"); + Some(format!("-Wl,-rpath,@loader_path/../{libdir}")) + } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") { + self.rustflags.arg("-Clink-args=-Wl,-z,origin"); + Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}")) + } else { + None + }; + if let Some(rpath) = rpath { + self.rustflags.arg(&format!("-Clink-args={rpath}")); + } + } + + for arg in linker_args(builder, compiler.host, LldThreads::Yes) { + self.hostflags.arg(&arg); + } + + if let Some(target_linker) = builder.linker(target) { + let target = crate::envify(&target.triple); + self.command.env(&format!("CARGO_TARGET_{target}_LINKER"), target_linker); + } + // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not + // `linker_args` here. + for flag in linker_flags(builder, target, LldThreads::Yes) { + self.rustflags.arg(&flag); + } + for arg in linker_args(builder, target, LldThreads::Yes) { + self.rustdocflags.arg(&arg); + } + + if !builder.config.dry_run() + && builder.cc.borrow()[&target].args().iter().any(|arg| arg == "-gz") + { + self.rustflags.arg("-Clink-arg=-gz"); + } + + // Throughout the build Cargo can execute a number of build scripts + // compiling C/C++ code and we need to pass compilers, archivers, flags, etc + // obtained previously to those build scripts. + // Build scripts use either the `cc` crate or `configure/make` so we pass + // the options through environment variables that are fetched and understood by both. + // + // FIXME: the guard against msvc shouldn't need to be here + if target.is_msvc() { + if let Some(ref cl) = builder.config.llvm_clang_cl { + self.command.env("CC", cl).env("CXX", cl); + } + } else { + let ccache = builder.config.ccache.as_ref(); + let ccacheify = |s: &Path| { + let ccache = match ccache { + Some(ref s) => s, + None => return s.display().to_string(), + }; + // FIXME: the cc-rs crate only recognizes the literal strings + // `ccache` and `sccache` when doing caching compilations, so we + // mirror that here. It should probably be fixed upstream to + // accept a new env var or otherwise work with custom ccache + // vars. + match &ccache[..] { + "ccache" | "sccache" => format!("{} {}", ccache, s.display()), + _ => s.display().to_string(), + } + }; + let triple_underscored = target.triple.replace("-", "_"); + let cc = ccacheify(&builder.cc(target)); + self.command.env(format!("CC_{triple_underscored}"), &cc); + + let cflags = builder.cflags(target, GitRepo::Rustc, CLang::C).join(" "); + self.command.env(format!("CFLAGS_{triple_underscored}"), &cflags); + + if let Some(ar) = builder.ar(target) { + let ranlib = format!("{} s", ar.display()); + self.command + .env(format!("AR_{triple_underscored}"), ar) + .env(format!("RANLIB_{triple_underscored}"), ranlib); + } + + if let Ok(cxx) = builder.cxx(target) { + let cxx = ccacheify(&cxx); + let cxxflags = builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" "); + self.command + .env(format!("CXX_{triple_underscored}"), &cxx) + .env(format!("CXXFLAGS_{triple_underscored}"), cxxflags); + } + } + + self + } } impl From for Command { diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index c0dd1e120848..4c64850c0e04 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1810,10 +1810,9 @@ impl Config { target.llvm_config = Some(config.src.join(s)); } if let Some(patches) = cfg.llvm_has_rust_patches { - assert_eq!( - config.submodules, - Some(false), - "cannot set `llvm-has-rust-patches` for a managed submodule (set `build.submodules = false` if you want to apply patches)" + assert!( + config.submodules == Some(false) || cfg.llvm_config.is_some(), + "use of `llvm-has-rust-patches` is restricted to cases where either submodules are disabled or llvm-config been provided" ); target.llvm_has_rust_patches = Some(patches); } diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 43e48c01176f..9b0f477409a9 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -514,7 +514,7 @@ jobs: RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set rust.lto=thin RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_13.4.1.app + SELECT_XCODE: /Applications/Xcode_14.3.1.app NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 @@ -528,7 +528,7 @@ jobs: RUST_CONFIGURE_ARGS: --enable-sanitizers --enable-profiler --set rust.jemalloc RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_13.4.1.app + SELECT_XCODE: /Applications/Xcode_14.3.1.app NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 @@ -541,6 +541,7 @@ jobs: RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 + SELECT_XCODE: /Applications/Xcode_14.3.1.app NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 @@ -564,7 +565,7 @@ jobs: --set llvm.ninja=false --set rust.lto=thin RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - SELECT_XCODE: /Applications/Xcode_13.4.1.app + SELECT_XCODE: /Applications/Xcode_14.3.1.app USE_XCODE_CLANG: 1 MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 @@ -583,7 +584,7 @@ jobs: --enable-profiler --set rust.jemalloc RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - SELECT_XCODE: /Applications/Xcode_13.4.1.app + SELECT_XCODE: /Applications/Xcode_14.3.1.app USE_XCODE_CLANG: 1 MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 diff --git a/src/ci/scripts/select-xcode.sh b/src/ci/scripts/select-xcode.sh index 3b9c77d42ba5..569c4a4136d9 100755 --- a/src/ci/scripts/select-xcode.sh +++ b/src/ci/scripts/select-xcode.sh @@ -7,7 +7,5 @@ IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" if isMacOS; then - if [[ -s "${SELECT_XCODE-}" ]]; then - sudo xcode-select -s "${SELECT_XCODE}" - fi + sudo xcode-select -s "${SELECT_XCODE}" fi diff --git a/src/doc/rustc/src/platform-support/armv6k-nintendo-3ds.md b/src/doc/rustc/src/platform-support/armv6k-nintendo-3ds.md index 2ce0ccb78769..540e5a4af938 100644 --- a/src/doc/rustc/src/platform-support/armv6k-nintendo-3ds.md +++ b/src/doc/rustc/src/platform-support/armv6k-nintendo-3ds.md @@ -10,6 +10,9 @@ from nor used with any official Nintendo SDK. ## Target maintainers +This target is maintained by members of the [@rust3ds](https://github.com/rust3ds) +organization: + - [@Meziu](https://github.com/Meziu) - [@AzureMarker](https://github.com/AzureMarker) - [@ian-h-chamberlain](https://github.com/ian-h-chamberlain) @@ -35,8 +38,8 @@ Additionally, some helper crates provide implementations of some `libc` function use by `std` that may otherwise be missing. These, or an alternate implementation of the relevant functions, are required to use `std`: -- [`pthread-3ds`](https://github.com/Meziu/pthread-3ds) provides pthread APIs for `std::thread`. -- [`linker-fix-3ds`](https://github.com/Meziu/rust-linker-fix-3ds) fulfills some other missing libc APIs. +- [`pthread-3ds`](https://github.com/rust3ds/pthread-3ds) provides pthread APIs for `std::thread`. +- [`shim-3ds`](https://github.com/rust3ds/shim-3ds) fulfills some other missing libc APIs (e.g. `getrandom`). Binaries built for this target should be compatible with all variants of the 3DS (and 2DS) hardware and firmware, but testing is limited and some versions may @@ -74,8 +77,10 @@ export CFLAGS_armv6k_nintendo_3ds="-mfloat-abi=hard -mtune=mpcore -mtp=soft -mar Rust does not yet ship pre-compiled artifacts for this target. The recommended way to build binaries is by using the -[cargo-3ds](https://github.com/Meziu/cargo-3ds) tool, which uses `build-std` +[cargo-3ds](https://github.com/rust3ds/cargo-3ds) tool, which uses `build-std` and provides commands that work like the usual `cargo run`, `cargo build`, etc. +The `cargo 3ds new` will automatically set up a new project with the dependencies +needed to build a simple binary. You can also build Rust with the target enabled (see [Building the target](#building-the-target) above). @@ -83,23 +88,16 @@ You can also build Rust with the target enabled (see As mentioned in [Requirements](#requirements), programs that use `std` must link against both the devkitARM toolchain and libraries providing the `libc` APIs used in `std`. There is a general-purpose utility crate for working with nonstandard -APIs provided by the OS: [`ctru-rs`](https://github.com/Meziu/ctru-rs). +APIs provided by the OS: [`ctru-rs`](https://github.com/rust3ds/ctru-rs). Add it to Cargo.toml to use it in your program: ```toml [dependencies] -ctru-rs = { git = "https://github.com/Meziu/ctru-rs.git" } +ctru-rs = { git = "https://github.com/rust3ds/ctru-rs.git" } ``` -Using this library's `init()` function ensures the symbols needed to link -against `std` are present (as mentioned in [Requirements](#requirements) -above), as well as providing a runtime suitable for `std`: - -```rust,ignore (requires-3rd-party-library) -fn main() { - ctru::init(); -} -``` +Depending on `ctru-rs` ensures that all the necessary symbols are available at +link time. ## Testing diff --git a/src/doc/unstable-book/src/library-features/duration-constructors.md b/src/doc/unstable-book/src/library-features/duration-constructors.md new file mode 100644 index 000000000000..098519c7c903 --- /dev/null +++ b/src/doc/unstable-book/src/library-features/duration-constructors.md @@ -0,0 +1,9 @@ +# `duration_constructors` + +The tracking issue for this feature is: [#120301] + +[#120301]: https://github.com/rust-lang/rust/issues/120301 + +------------------------ + +Add the methods `from_mins`, `from_hours` and `from_days` to `Duration`. diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 7ca4392233ed..23a059c6e030 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -65,37 +65,49 @@ pub(crate) fn try_inline( let kind = match res { Res::Def(DefKind::Trait, did) => { record_extern_fqn(cx, did, ItemType::Trait); - build_impls(cx, did, attrs_without_docs, &mut ret); - clean::TraitItem(Box::new(build_external_trait(cx, did))) + cx.with_param_env(did, |cx| { + build_impls(cx, did, attrs_without_docs, &mut ret); + clean::TraitItem(Box::new(build_external_trait(cx, did))) + }) } Res::Def(DefKind::Fn, did) => { record_extern_fqn(cx, did, ItemType::Function); - clean::FunctionItem(build_external_function(cx, did)) + cx.with_param_env(did, |cx| clean::FunctionItem(build_external_function(cx, did))) } Res::Def(DefKind::Struct, did) => { record_extern_fqn(cx, did, ItemType::Struct); - build_impls(cx, did, attrs_without_docs, &mut ret); - clean::StructItem(build_struct(cx, did)) + cx.with_param_env(did, |cx| { + build_impls(cx, did, attrs_without_docs, &mut ret); + clean::StructItem(build_struct(cx, did)) + }) } Res::Def(DefKind::Union, did) => { record_extern_fqn(cx, did, ItemType::Union); - build_impls(cx, did, attrs_without_docs, &mut ret); - clean::UnionItem(build_union(cx, did)) + cx.with_param_env(did, |cx| { + build_impls(cx, did, attrs_without_docs, &mut ret); + clean::UnionItem(build_union(cx, did)) + }) } Res::Def(DefKind::TyAlias, did) => { record_extern_fqn(cx, did, ItemType::TypeAlias); - build_impls(cx, did, attrs_without_docs, &mut ret); - clean::TypeAliasItem(build_type_alias(cx, did, &mut ret)) + cx.with_param_env(did, |cx| { + build_impls(cx, did, attrs_without_docs, &mut ret); + clean::TypeAliasItem(build_type_alias(cx, did, &mut ret)) + }) } Res::Def(DefKind::Enum, did) => { record_extern_fqn(cx, did, ItemType::Enum); - build_impls(cx, did, attrs_without_docs, &mut ret); - clean::EnumItem(build_enum(cx, did)) + cx.with_param_env(did, |cx| { + build_impls(cx, did, attrs_without_docs, &mut ret); + clean::EnumItem(build_enum(cx, did)) + }) } Res::Def(DefKind::ForeignTy, did) => { record_extern_fqn(cx, did, ItemType::ForeignType); - build_impls(cx, did, attrs_without_docs, &mut ret); - clean::ForeignTypeItem + cx.with_param_env(did, |cx| { + build_impls(cx, did, attrs_without_docs, &mut ret); + clean::ForeignTypeItem + }) } // Never inline enum variants but leave them shown as re-exports. Res::Def(DefKind::Variant, _) => return None, @@ -108,11 +120,13 @@ pub(crate) fn try_inline( } Res::Def(DefKind::Static(_), did) => { record_extern_fqn(cx, did, ItemType::Static); - clean::StaticItem(build_static(cx, did, cx.tcx.is_mutable_static(did))) + cx.with_param_env(did, |cx| { + clean::StaticItem(build_static(cx, did, cx.tcx.is_mutable_static(did))) + }) } Res::Def(DefKind::Const, did) => { record_extern_fqn(cx, did, ItemType::Constant); - clean::ConstantItem(build_const(cx, did)) + cx.with_param_env(did, |cx| clean::ConstantItem(build_const(cx, did))) } Res::Def(DefKind::Macro(kind), did) => { let mac = build_macro(cx, did, name, import_def_id, kind); @@ -334,7 +348,9 @@ pub(crate) fn build_impls( // for each implementation of an item represented by `did`, build the clean::Item for that impl for &did in tcx.inherent_impls(did).into_iter().flatten() { - build_impl(cx, did, attrs, ret); + cx.with_param_env(did, |cx| { + build_impl(cx, did, attrs, ret); + }); } // This pretty much exists expressly for `dyn Error` traits that exist in the `alloc` crate. @@ -347,7 +363,9 @@ pub(crate) fn build_impls( let type_ = if tcx.is_trait(did) { SimplifiedType::Trait(did) } else { SimplifiedType::Adt(did) }; for &did in tcx.incoherent_impls(type_).into_iter().flatten() { - build_impl(cx, did, attrs, ret); + cx.with_param_env(did, |cx| { + build_impl(cx, did, attrs, ret); + }); } } } @@ -549,7 +567,9 @@ pub(crate) fn build_impl( } if let Some(did) = trait_.as_ref().map(|t| t.def_id()) { - record_extern_trait(cx, did); + cx.with_param_env(did, |cx| { + record_extern_trait(cx, did); + }); } let (merged_attrs, cfg) = merge_attrs(cx, load_attrs(cx, did), attrs); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b8e0d75e7dc3..89977934cde9 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -947,7 +947,9 @@ fn clean_ty_alias_inner_type<'tcx>( }; if !adt_def.did().is_local() { - inline::build_impls(cx, adt_def.did(), None, ret); + cx.with_param_env(adt_def.did(), |cx| { + inline::build_impls(cx, adt_def.did(), None, ret); + }); } Some(if adt_def.is_enum() { diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index b2b20c95a7e6..0b7d35d7be4c 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -295,12 +295,16 @@ pub(crate) fn build_deref_target_impls( if let Some(prim) = target.primitive_type() { let _prof_timer = tcx.sess.prof.generic_activity("build_primitive_inherent_impls"); for did in prim.impls(tcx).filter(|did| !did.is_local()) { - inline::build_impl(cx, did, None, ret); + cx.with_param_env(did, |cx| { + inline::build_impl(cx, did, None, ret); + }); } } else if let Type::Path { path } = target { let did = path.def_id(); if !did.is_local() { - inline::build_impls(cx, did, None, ret); + cx.with_param_env(did, |cx| { + inline::build_impls(cx, did, None, ret); + }); } } } diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index e2a7ef8556ea..53c08ef5e5ca 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -49,7 +49,9 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> let _prof_timer = tcx.sess.prof.generic_activity("build_extern_trait_impls"); for &cnum in tcx.crates(()) { for &impl_def_id in tcx.trait_impls_in_crate(cnum) { - inline::build_impl(cx, impl_def_id, None, &mut new_items_external); + cx.with_param_env(impl_def_id, |cx| { + inline::build_impl(cx, impl_def_id, None, &mut new_items_external); + }); } } } @@ -74,7 +76,9 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> ); parent = tcx.opt_parent(did); } - inline::build_impl(cx, impl_def_id, Some((&attr_buf, None)), &mut new_items_local); + cx.with_param_env(impl_def_id, |cx| { + inline::build_impl(cx, impl_def_id, Some((&attr_buf, None)), &mut new_items_local); + }); attr_buf.clear(); } } @@ -83,7 +87,9 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> for def_id in PrimitiveType::all_impls(tcx) { // Try to inline primitive impls from other crates. if !def_id.is_local() { - inline::build_impl(cx, def_id, None, &mut new_items_external); + cx.with_param_env(def_id, |cx| { + inline::build_impl(cx, def_id, None, &mut new_items_external); + }); } } for (prim, did) in PrimitiveType::primitive_locations(tcx) { diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs index 1e327f7a6dfb..9365fbfaed08 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs @@ -46,11 +46,10 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { return; }; if let ConstantSource::Constant = source - && let Some(node) = cx.tcx.hir().find_parent(e.hir_id) && let Node::Item(Item { kind: ItemKind::Const(..), .. - }) = node + }) = cx.tcx.parent_hir_node(e.hir_id) { return; } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs index 0f29743856ac..a31943f00218 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -67,25 +67,20 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv } fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let map = cx.tcx.hir(); - if let Some(parent_id) = map.opt_parent_id(expr.hir_id) { - let parent = cx.tcx.hir_node(parent_id); - let expr = match parent { - Node::Block(block) => { - if let Some(parent_expr) = block.expr { - parent_expr - } else { - return false; - } - }, - Node::Expr(expr) => expr, - _ => return false, - }; + let parent = cx.tcx.parent_hir_node(expr.hir_id); + let expr = match parent { + Node::Block(block) => { + if let Some(parent_expr) = block.expr { + parent_expr + } else { + return false; + } + }, + Node::Expr(expr) => expr, + _ => return false, + }; - matches!(expr.kind, ExprKind::Cast(..)) - } else { - false - } + matches!(expr.kind, ExprKind::Cast(..)) } /// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index 81d0def4322d..b4a23d0d4db4 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -65,7 +65,7 @@ pub(super) fn check<'tcx>( && let ExprKind::Path(qpath) = inner.kind && let QPath::Resolved(None, Path { res, .. }) = qpath && let Res::Local(hir_id) = res - && let parent = cx.tcx.hir().get_parent(*hir_id) + && let parent = cx.tcx.parent_hir_node(*hir_id) && let Node::Local(local) = parent { if let Some(ty) = local.ty diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index 9424a9103db8..ec66556cebff 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -63,7 +63,7 @@ impl LateLintPass<'_> for DbgMacro { ExprKind::Block(..) => { // If the `dbg!` macro is a "free" statement and not contained within other expressions, // remove the whole statement. - if let Some(Node::Stmt(_)) = cx.tcx.hir().find_parent(expr.hir_id) + if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) { (macro_call.span.to(semi_span), String::new()) diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index d8a070b785d5..8789efcc9944 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -131,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { // only when assigning `... = Default::default()` && is_expr_default(expr, cx) && let binding_type = cx.typeck_results().node_type(binding_id) - && let Some(adt) = binding_type.ty_adt_def() + && let ty::Adt(adt, args) = *binding_type.kind() && adt.is_struct() && let variant = adt.non_enum_variant() && (adt.did().is_local() || !variant.is_field_list_non_exhaustive()) @@ -144,7 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { .fields .iter() .all(|field| { - is_copy(cx, cx.tcx.type_of(field.did).instantiate_identity()) + is_copy(cx, cx.tcx.type_of(field.did).instantiate(cx.tcx, args)) }) && (!has_drop(cx, binding_type) || all_fields_are_copy) { diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index c4437a3c4b33..59d2df0295fb 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -128,8 +128,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { }, _, ) => { - if let Some(parent) = self.cx.tcx.hir().find_parent(expr.hir_id) - && let Some(fn_sig) = parent.fn_sig() + if let Some(fn_sig) = self.cx.tcx.parent_hir_node(expr.hir_id).fn_sig() && let FnRetTy::Return(_ty) = fn_sig.decl.output { // We cannot check the exact type since it's a `hir::Ty`` which does not implement `is_numeric` diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 194cf69ea7ed..cdbb52f497b3 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -1088,7 +1088,7 @@ fn report<'tcx>( // // e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a // deref through `ManuallyDrop<_>` will not compile. - let parent_id = cx.tcx.hir().parent_id(expr.hir_id); + let parent_id = cx.tcx.parent_hir_id(expr.hir_id); if parent_id == data.first_expr.hir_id { return; } diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs index 218d7c6c01ae..064bac2e7dc7 100644 --- a/src/tools/clippy/clippy_lints/src/escape.rs +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -131,7 +131,7 @@ fn is_argument(tcx: TyCtxt<'_>, id: HirId) -> bool { _ => return false, } - matches!(tcx.hir().find_parent(id), Some(Node::Param(_))) + matches!(tcx.parent_hir_node(id), Node::Param(_)) } impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { @@ -156,8 +156,8 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { let map = &self.cx.tcx.hir(); if is_argument(self.cx.tcx, cmt.hir_id) { // Skip closure arguments - let parent_id = map.parent_id(cmt.hir_id); - if let Some(Node::Expr(..)) = map.find_parent(parent_id) { + let parent_id = self.cx.tcx.parent_hir_id(cmt.hir_id); + if let Node::Expr(..) = self.cx.tcx.parent_hir_node(parent_id) { return; } diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs index 8fba41c0e24d..6fb38a0d6dd8 100644 --- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs +++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs @@ -53,7 +53,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { if let ImplItemKind::Fn(_, body_id) = impl_item.kind - && let hir::Node::Item(item) = cx.tcx.hir().get_parent(impl_item.hir_id()) + && let hir::Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) && let hir::ItemKind::Impl(impl_) = item.kind && let hir::Impl { of_trait, .. } = *impl_ && of_trait.is_none() @@ -72,7 +72,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>, avoid_breaking_exported_api: bool) { if !avoid_breaking_exported_api && let TraitItemKind::Fn(_, _) = trait_item.kind - && let hir::Node::Item(item) = cx.tcx.hir().get_parent(trait_item.hir_id()) + && let hir::Node::Item(item) = cx.tcx.parent_hir_node(trait_item.hir_id()) // ^^ (Will always be a trait) && !item.vis_span.is_empty() // Is public && !is_in_test_function(cx.tcx, trait_item.hir_id()) diff --git a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs index 0a2fd0c663e5..80a537b9f941 100644 --- a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs @@ -41,8 +41,8 @@ impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns { return; } - match cx.tcx.hir().get_parent(pat.hir_id) { - Node::Param(param) if matches!(cx.tcx.hir().get_parent(param.hir_id), Node::Item(_)) => { + match cx.tcx.parent_hir_node(pat.hir_id) { + Node::Param(param) if matches!(cx.tcx.parent_hir_node(param.hir_id), Node::Item(_)) => { // Ignore function parameters return; }, diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 252be30c4e27..51b4f26b6d13 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -242,12 +242,8 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { } = *self; if let Some(use_info) = slice_lint_info.get_mut(&local_id) - // Check if this is even a local we're interested in - - && let map = cx.tcx.hir() - // Checking for slice indexing - && let parent_id = map.parent_id(expr.hir_id) + && let parent_id = cx.tcx.parent_hir_id(expr.hir_id) && let hir::Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) && let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind && let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr) @@ -255,11 +251,10 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { && index_value < max_suggested_slice // Make sure that this slice index is read only - && let maybe_addrof_id = map.parent_id(parent_id) - && let hir::Node::Expr(maybe_addrof_expr) = cx.tcx.hir_node(maybe_addrof_id) + && let hir::Node::Expr(maybe_addrof_expr) = cx.tcx.parent_hir_node(parent_id) && let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind { - use_info.index_use.push((index_value, map.span(parent_expr.hir_id))); + use_info.index_use.push((index_value, cx.tcx.hir().span(parent_expr.hir_id))); return; } diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs index 82a37bb4f278..b5821d909f84 100644 --- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -269,7 +269,7 @@ impl {self_ty_without_ref} {{ // } let span_behind_impl = cx .tcx - .def_span(cx.tcx.hir().parent_id(item.hir_id()).owner.def_id) + .def_span(cx.tcx.parent_hir_id(item.hir_id()).owner.def_id) .shrink_to_lo(); let sugg = format!( diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index f5636945f203..2b73663d229e 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -285,7 +285,7 @@ fn elision_suggestions( .iter() .filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id))) .map(|usage| { - match cx.tcx.hir().get_parent(usage.hir_id) { + match cx.tcx.parent_hir_node(usage.hir_id) { Node::Ty(Ty { kind: TyKind::Ref(..), .. }) => { diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs index 5f015db2b33b..0f35514b8ad6 100644 --- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -62,8 +62,7 @@ pub(super) fn check<'tcx>( if let Node::Pat(pat) = node && let PatKind::Binding(bind_ann, ..) = pat.kind && !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut)) - && let parent_node = cx.tcx.hir().parent_id(hir_id) - && let Node::Local(parent_let_expr) = cx.tcx.hir_node(parent_node) + && let Node::Local(parent_let_expr) = cx.tcx.parent_hir_node(hir_id) && let Some(init) = parent_let_expr.init { match init.kind { diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs index 73687fbbe54e..5cbab0ec977c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs +++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs @@ -68,8 +68,8 @@ impl LateLintPass<'_> for ManualHashOne { && let ExprKind::MethodCall(seg, build_hasher, [], _) = init.kind && seg.ident.name == sym!(build_hasher) - && let Node::Stmt(local_stmt) = cx.tcx.hir().get_parent(local.hir_id) - && let Node::Block(block) = cx.tcx.hir().get_parent(local_stmt.hir_id) + && let Node::Stmt(local_stmt) = cx.tcx.parent_hir_node(local.hir_id) + && let Node::Block(block) = cx.tcx.parent_hir_node(local_stmt.hir_id) && let mut stmts = block.stmts.iter() .skip_while(|stmt| stmt.hir_id != local_stmt.hir_id) @@ -91,7 +91,7 @@ impl LateLintPass<'_> for ManualHashOne { // `hasher.finish()`, may be anywhere in a statement or the trailing expr of the block && let Some(path_expr) = local_used_once(cx, (maybe_finish_stmt, block.expr), hasher) - && let Node::Expr(finish_expr) = cx.tcx.hir().get_parent(path_expr.hir_id) + && let Node::Expr(finish_expr) = cx.tcx.parent_hir_node(path_expr.hir_id) && !finish_expr.span.from_expansion() && let ExprKind::MethodCall(seg, _, [], _) = finish_expr.kind && seg.ident.name == sym!(finish) diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs index e1768c6d9764..0bde62bd5549 100644 --- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs +++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs @@ -79,9 +79,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { && let Node::Pat(_) = cx.tcx.hir_node(hir_id) { // Apply only to params or locals with annotated types - match cx.tcx.hir().find_parent(hir_id) { - Some(Node::Param(..)) => (), - Some(Node::Local(local)) => { + match cx.tcx.parent_hir_node(hir_id) { + Node::Param(..) => (), + Node::Local(local) => { let Some(ty) = local.ty else { return }; if matches!(ty.kind, TyKind::Infer) { return; diff --git a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs index 89da7a55cbd5..61977045fd46 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs @@ -36,7 +36,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e .to_string(); // Do we need to add ';' to suggestion ? - if let Node::Stmt(stmt) = cx.tcx.hir().get_parent(expr.hir_id) + if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) && let StmtKind::Expr(_) = stmt.kind && match match_body.kind { // We don't need to add a ; to blocks, unless that block is from a macro expansion @@ -146,18 +146,16 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e /// Returns true if the `ex` match expression is in a local (`let`) or assign expression fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option { - let map = &cx.tcx.hir(); - - if let Some(Node::Expr(parent_arm_expr)) = map.find_parent(ex.hir_id) { - return match map.find_parent(parent_arm_expr.hir_id) { - Some(Node::Local(parent_let_expr)) => Some(AssignmentExpr::Local { + if let Node::Expr(parent_arm_expr) = cx.tcx.parent_hir_node(ex.hir_id) { + return match cx.tcx.parent_hir_node(parent_arm_expr.hir_id) { + Node::Local(parent_let_expr) => Some(AssignmentExpr::Local { span: parent_let_expr.span, pat_span: parent_let_expr.pat.span(), }), - Some(Node::Expr(Expr { + Node::Expr(Expr { kind: ExprKind::Assign(parent_assign_expr, match_expr, _), .. - })) => Some(AssignmentExpr::Assign { + }) => Some(AssignmentExpr::Assign { span: parent_assign_expr.span, match_span: match_expr.span, }), @@ -191,7 +189,7 @@ fn sugg_with_curlies<'a>( // If the parent is already an arm, and the body is another match statement, // we need curly braces around suggestion - if let Node::Arm(arm) = &cx.tcx.hir().get_parent(match_expr.hir_id) { + if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id) { if let ExprKind::Match(..) = arm.body.kind { cbrace_end = format!("\n{indent}}}"); // Fix body indent due to the match diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index dfaaeb14ca3c..a1b82679f2e2 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -199,7 +199,7 @@ fn get_pat_binding<'tcx>( return span.map(|span| PatBindingInfo { span, byref_ident, - is_field: matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)), + is_field: matches!(cx.tcx.parent_hir_node(local), Node::PatField(_)), }); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index e8a7a321bf4b..1452547807ba 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -4458,7 +4458,7 @@ impl Methods { _ => {}, }, ("drain", ..) => { - if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.hir().get_parent(expr.hir_id) + if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.parent_hir_node(expr.hir_id) && matches!(kind, StmtKind::Semi(_)) && args.len() <= 1 { diff --git a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs index 1184dd4525a7..6c6846c4b476 100644 --- a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs +++ b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs @@ -21,9 +21,9 @@ fn is_unwrap_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver: &Expr<'_>) { if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::RwLock) - && let Node::Expr(unwrap_call_expr) = cx.tcx.hir().get_parent(expr.hir_id) + && let Node::Expr(unwrap_call_expr) = cx.tcx.parent_hir_node(expr.hir_id) && is_unwrap_call(cx, unwrap_call_expr) - && let parent = cx.tcx.hir().get_parent(unwrap_call_expr.hir_id) + && let parent = cx.tcx.parent_hir_node(unwrap_call_expr.hir_id) && let Node::Local(local) = parent && let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id) && let Some((local, _)) = mir diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs index 2046692bbd0b..988f3e86fcf0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs @@ -16,7 +16,7 @@ use super::UNNECESSARY_FOLD; /// Changing `fold` to `sum` needs it sometimes when the return type can't be /// inferred. This checks for some common cases where it can be safely omitted fn needs_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - let parent = cx.tcx.hir().get_parent(expr.hir_id); + let parent = cx.tcx.parent_hir_node(expr.hir_id); // some common cases where turbofish isn't needed: // - assigned to a local variable with a type annotation diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index a1125d70db39..1b2bfbf4090e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -76,7 +76,7 @@ pub(super) fn check( (expr.span.with_lo(call_args[0].span.hi()), String::new()), ]; // try to also remove the unsafe block if present - if let hir::Node::Block(block) = cx.tcx.hir().get_parent(expr.hir_id) + if let hir::Node::Block(block) = cx.tcx.parent_hir_node(expr.hir_id) && let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules { suggs.extend([ diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index b593e48ae2e1..a1f7dc7b38c4 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -206,10 +206,9 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { /// /// When such a read is found, the lint is triggered. fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) { - let map = &vis.cx.tcx.hir(); let mut cur_id = vis.write_expr.hir_id; loop { - let parent_id = map.parent_id(cur_id); + let parent_id = vis.cx.tcx.parent_hir_id(cur_id); if parent_id == cur_id { break; } diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index d2eef6ae4338..149d440ecac4 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -161,7 +161,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { }; // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 2c5c3dcaa752..384a402ce5b0 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -100,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 10ab380ba1bc..ea73d9afa2ea 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -449,7 +449,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { let mut dereferenced_expr = expr; let mut needs_check_adjustment = true; loop { - let parent_id = cx.tcx.hir().parent_id(cur_expr.hir_id); + let parent_id = cx.tcx.parent_hir_id(cur_expr.hir_id); if parent_id == cur_expr.hir_id { break; } diff --git a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs index 40d4a842befb..2a933a11e12c 100644 --- a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>( } fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let Some(Node::Expr(parent_expr)) = cx.tcx.hir().find_parent(expr.hir_id) else { + let Node::Expr(parent_expr) = cx.tcx.parent_hir_node(expr.hir_id) else { return false; }; let ExprKind::Binary(op, lhs, rhs) = parent_expr.kind else { diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs index 57d37067e8f9..ec03ab0e41ab 100644 --- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs +++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs @@ -301,7 +301,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 513a913f56ad..bbecc39a8130 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -41,7 +41,7 @@ pub(super) fn check<'tcx>( _ => return false, }; - if let Node::Expr(parent) = cx.tcx.hir().get_parent(e.hir_id) + if let Node::Expr(parent) = cx.tcx.parent_hir_node(e.hir_id) && parent.precedence().order() > ExprPrecedence::Cast.order() { sugg = format!("({sugg})"); diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs index e1cd82e18d56..c11504cd2d4f 100644 --- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs @@ -153,13 +153,10 @@ fn all_bindings_are_for_conv<'tcx>( let Some(locals) = locals.iter().map(|e| path_to_local(e)).collect::>>() else { return false; }; - let Some(local_parents) = locals + let local_parents = locals .iter() - .map(|&l| cx.tcx.hir().find_parent(l)) - .collect::>>() - else { - return false; - }; + .map(|l| cx.tcx.parent_hir_node(*l)) + .collect::>(); local_parents .iter() diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs index 44cff78a7936..eba7fa7b993c 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs @@ -19,9 +19,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if is_questionmark_desugar_marked_call(expr) { return; } - let map = &cx.tcx.hir(); - let opt_parent_node = map.find_parent(expr.hir_id); - if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node + if let hir::Node::Expr(parent_expr) = cx.tcx.parent_hir_node(expr.hir_id) && is_questionmark_desugar_marked_call(parent_expr) { return; @@ -183,8 +181,8 @@ fn fmt_stmts_and_call( let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent))); // expr is not in a block statement or result expression position, wrap in a block - let parent_node = cx.tcx.hir().find_parent(call_expr.hir_id); - if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { + let parent_node = cx.tcx.parent_hir_node(call_expr.hir_id); + if !matches!(parent_node, Node::Block(_)) && !matches!(parent_node, Node::Stmt(_)) { let block_indent = call_expr_indent + 4; stmts_and_call_snippet = reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned(); diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs index f5af540fa148..c332cf076ae7 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs @@ -116,7 +116,7 @@ impl LateLintPass<'_> for UnnecessaryBoxReturns { fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) { // Ignore implementations of traits, because the lint should be on the // trait, not on the implementation of it. - let Node::Item(parent) = cx.tcx.hir().get_parent(item.hir_id()) else { + let Node::Item(parent) = cx.tcx.parent_hir_node(item.hir_id()) else { return; }; let ItemKind::Impl(parent) = parent.kind else { return }; diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index 446160f8e0fd..9c8b0ae17276 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { // Abort if the method is implementing a trait or of it a trait method. let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs index 1d42375ba8e5..738fba54fa83 100644 --- a/src/tools/clippy/clippy_lints/src/unused_async.rs +++ b/src/tools/clippy/clippy_lints/src/unused_async.rs @@ -156,7 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { && let Some(local_def_id) = def_id.as_local() && cx.tcx.def_kind(def_id) == DefKind::Fn && cx.tcx.asyncness(def_id).is_async() - && !is_node_func_call(cx.tcx.hir().get_parent(hir_id), path.span) + && !is_node_func_call(cx.tcx.parent_hir_node(hir_id), path.span) { self.async_fns_as_value.insert(local_def_id); } diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index ae2ac38cffe1..f2eb774b5cbf 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -208,7 +208,7 @@ struct MutationVisitor<'tcx> { /// (i.e. the `x` in `x.as_mut()`), and that is the reason for why we care about its parent /// expression: that will be where the actual method call is. fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { - if let Node::Expr(mutating_expr) = tcx.hir().get_parent(expr_id) + if let Node::Expr(mutating_expr) = tcx.parent_hir_node(expr_id) && let ExprKind::MethodCall(path, ..) = mutating_expr.kind { path.ident.name.as_str() == "as_mut" diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 2e0a0f6cb3e4..f7a455977fac 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -86,7 +86,6 @@ fn into_iter_bound<'tcx>( param_index: u32, node_args: GenericArgsRef<'tcx>, ) -> Option { - let param_env = cx.tcx.param_env(fn_did); let mut into_iter_span = None; for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates { @@ -111,7 +110,7 @@ fn into_iter_bound<'tcx>( })); let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); - let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), param_env, predicate); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); if !cx .tcx .infer_ctxt() diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index fae1b90ace21..349e0e3e077a 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -1007,9 +1007,9 @@ fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) - fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { let map = cx.tcx.hir(); - match map.find_parent(hir_id) { - Some(hir::Node::Local(local)) => Some(local), - Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id), + match cx.tcx.parent_hir_node(hir_id) { + hir::Node::Local(local) => Some(local), + hir::Node::Pat(pattern) => get_parent_local_hir_id(cx, pattern.hir_id), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 6e449dc98063..38c832931fc6 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -217,8 +217,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option match cx.qpath_res(qpath, expr.hir_id) { Res::Local(hir_id) => { - let parent_id = cx.tcx.hir().parent_id(hir_id); - if let Node::Local(Local { init: Some(init), .. }) = cx.tcx.hir_node(parent_id) { + if let Node::Local(Local { init: Some(init), .. }) = cx.tcx.parent_hir_node(hir_id) { path_to_matched_type(cx, init) } else { None diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index 2c33c93412a3..b3489142558e 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) { // search for `let foo = vec![_]` expressions where all uses of `foo` // adjust to slices or call a method that exist on slices (e.g. len) - if let Node::Local(local) = cx.tcx.hir().get_parent(expr.hir_id) + if let Node::Local(local) = cx.tcx.parent_hir_node(expr.hir_id) // for now ignore locals with type annotations. // this is to avoid compile errors when doing the suggestion here: let _: Vec<_> = vec![..]; && local.ty.is_none() @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { } // if the local pattern has a specified type, do not lint. else if let Some(_) = higher::VecArgs::hir(cx, expr) - && let Node::Local(local) = cx.tcx.hir().get_parent(expr.hir_id) + && let Node::Local(local) = cx.tcx.parent_hir_node(expr.hir_id) && local.ty.is_some() { let span = expr.span.ctxt().outer_expn_data().call_site; diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 6a33e11be465..67a18caca0e2 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -182,11 +182,9 @@ pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr /// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the /// canonical binding `HirId`. pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> { - let hir = cx.tcx.hir(); if let Node::Pat(pat) = cx.tcx.hir_node(hir_id) && matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..)) - && let parent = hir.parent_id(hir_id) - && let Node::Local(local) = cx.tcx.hir_node(parent) + && let Node::Local(local) = cx.tcx.parent_hir_node(hir_id) { return local.init; } @@ -333,7 +331,7 @@ pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) /// Checks if the `def_id` belongs to a function that is part of a trait impl. pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { if let Some(hir_id) = cx.tcx.opt_local_def_id_to_hir_id(def_id) - && let Node::Item(item) = cx.tcx.hir().get_parent(hir_id) + && let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) && let ItemKind::Impl(imp) = item.kind { imp.of_trait.is_some() @@ -1311,7 +1309,7 @@ pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool { /// Gets the parent node, if any. pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option> { - tcx.hir().find_parent(id) + Some(tcx.parent_hir_node(id)) } /// Gets the parent expression, if any –- this is useful to constrain a lint. @@ -2227,7 +2225,7 @@ pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { /// } /// ``` pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool { - if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })) } else { false diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index adca2ca1c3ef..7913926928f2 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -237,7 +237,7 @@ fn path_segment_certainty( }, // `get_parent` because `hir_id` refers to a `Pat`, and we're interested in the node containing the `Pat`. - Res::Local(hir_id) => match cx.tcx.hir().get_parent(hir_id) { + Res::Local(hir_id) => match cx.tcx.parent_hir_node(hir_id) { // An argument's type is always certain. Node::Param(..) => Certainty::Certain(None), // A local's type is certain if its type annotation is certain or it has an initializer whose diff --git a/src/tools/linkchecker/Cargo.toml b/src/tools/linkchecker/Cargo.toml index 1d8f2f918823..318a69ab8359 100644 --- a/src/tools/linkchecker/Cargo.toml +++ b/src/tools/linkchecker/Cargo.toml @@ -10,3 +10,4 @@ path = "main.rs" [dependencies] regex = "1" once_cell = "1" +html5ever = "0.26.0" diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 7f73cac63cbd..f49c6e79f13c 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -14,6 +14,12 @@ //! A few exceptions are allowed as there's known bugs in rustdoc, but this //! should catch the majority of "broken link" cases. +use html5ever::tendril::ByteTendril; +use html5ever::tokenizer::{ + BufferQueue, TagToken, Token, TokenSink, TokenSinkResult, Tokenizer, TokenizerOpts, +}; +use once_cell::sync::Lazy; +use regex::Regex; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::env; @@ -23,9 +29,6 @@ use std::path::{Component, Path, PathBuf}; use std::rc::Rc; use std::time::Instant; -use once_cell::sync::Lazy; -use regex::Regex; - // Add linkcheck exceptions here // If at all possible you should use intra-doc links to avoid linkcheck issues. These // are cases where that does not work @@ -182,163 +185,10 @@ impl Checker { } }; - // Search for anything that's the regex 'href[ ]*=[ ]*".*?"' - with_attrs_in_source(&source, " href", |url, i, base| { - // Ignore external URLs - if url.starts_with("http:") - || url.starts_with("https:") - || url.starts_with("javascript:") - || url.starts_with("ftp:") - || url.starts_with("irc:") - || url.starts_with("data:") - || url.starts_with("mailto:") - { - report.links_ignored_external += 1; - return; - } - report.links_checked += 1; - let (url, fragment) = match url.split_once('#') { - None => (url, None), - Some((url, fragment)) => (url, Some(fragment)), - }; - // NB: the `splitn` always succeeds, even if the delimiter is not present. - let url = url.splitn(2, '?').next().unwrap(); - - // Once we've plucked out the URL, parse it using our base url and - // then try to extract a file path. - let mut path = file.to_path_buf(); - if !base.is_empty() || !url.is_empty() { - path.pop(); - for part in Path::new(base).join(url).components() { - match part { - Component::Prefix(_) | Component::RootDir => { - // Avoid absolute paths as they make the docs not - // relocatable by making assumptions on where the docs - // are hosted relative to the site root. - report.errors += 1; - println!( - "{}:{}: absolute path - {}", - pretty_path, - i + 1, - Path::new(base).join(url).display() - ); - return; - } - Component::CurDir => {} - Component::ParentDir => { - path.pop(); - } - Component::Normal(s) => { - path.push(s); - } - } - } - } - - let (target_pretty_path, target_entry) = self.load_file(&path, report); - let (target_source, target_ids) = match target_entry { - FileEntry::Missing => { - if is_exception(file, &target_pretty_path) { - report.links_ignored_exception += 1; - } else { - report.errors += 1; - println!( - "{}:{}: broken link - `{}`", - pretty_path, - i + 1, - target_pretty_path - ); - } - return; - } - FileEntry::Dir => { - // Links to directories show as directory listings when viewing - // the docs offline so it's best to avoid them. - report.errors += 1; - println!( - "{}:{}: directory link to `{}` \ - (directory links should use index.html instead)", - pretty_path, - i + 1, - target_pretty_path - ); - return; - } - FileEntry::OtherFile => return, - FileEntry::Redirect { target } => { - let t = target.clone(); - let (target, redir_entry) = self.load_file(&t, report); - match redir_entry { - FileEntry::Missing => { - report.errors += 1; - println!( - "{}:{}: broken redirect from `{}` to `{}`", - pretty_path, - i + 1, - target_pretty_path, - target - ); - return; - } - FileEntry::Redirect { target } => { - // Redirect to a redirect, this link checker - // currently doesn't support this, since it would - // require cycle checking, etc. - report.errors += 1; - println!( - "{}:{}: redirect from `{}` to `{}` \ - which is also a redirect (not supported)", - pretty_path, - i + 1, - target_pretty_path, - target.display() - ); - return; - } - FileEntry::Dir => { - report.errors += 1; - println!( - "{}:{}: redirect from `{}` to `{}` \ - which is a directory \ - (directory links should use index.html instead)", - pretty_path, - i + 1, - target_pretty_path, - target - ); - return; - } - FileEntry::OtherFile => return, - FileEntry::HtmlFile { source, ids } => (source, ids), - } - } - FileEntry::HtmlFile { source, ids } => (source, ids), - }; - - // Alright, if we've found an HTML file for the target link. If - // this is a fragment link, also check that the `id` exists. - if let Some(ref fragment) = fragment { - // Fragments like `#1-6` are most likely line numbers to be - // interpreted by javascript, so we're ignoring these - if fragment.splitn(2, '-').all(|f| f.chars().all(|c| c.is_numeric())) { - return; - } - - parse_ids(&mut target_ids.borrow_mut(), &pretty_path, target_source, report); - - if target_ids.borrow().contains(*fragment) { - return; - } - - if is_exception(file, &format!("#{}", fragment)) { - report.links_ignored_exception += 1; - } else { - report.errors += 1; - print!("{}:{}: broken link fragment ", pretty_path, i + 1); - println!("`#{}` pointing to `{}`", fragment, target_pretty_path); - }; - } - }); + let (base, urls) = get_urls(&source); + for (i, url) in urls { + self.check_url(file, &pretty_path, report, &base, i, &url); + } self.check_intra_doc_links(file, &pretty_path, &source, report); @@ -350,6 +200,159 @@ impl Checker { } } + fn check_url( + &mut self, + file: &Path, + pretty_path: &str, + report: &mut Report, + base: &Option, + i: u64, + url: &str, + ) { + // Ignore external URLs + if url.starts_with("http:") + || url.starts_with("https:") + || url.starts_with("javascript:") + || url.starts_with("ftp:") + || url.starts_with("irc:") + || url.starts_with("data:") + || url.starts_with("mailto:") + { + report.links_ignored_external += 1; + return; + } + report.links_checked += 1; + let (url, fragment) = match url.split_once('#') { + None => (url, None), + Some((url, fragment)) => (url, Some(fragment)), + }; + // NB: the `splitn` always succeeds, even if the delimiter is not present. + let url = url.splitn(2, '?').next().unwrap(); + + // Once we've plucked out the URL, parse it using our base url and + // then try to extract a file path. + let mut path = file.to_path_buf(); + if base.is_some() || !url.is_empty() { + let base = base.as_deref().unwrap_or(""); + path.pop(); + for part in Path::new(base).join(url).components() { + match part { + Component::Prefix(_) | Component::RootDir => { + // Avoid absolute paths as they make the docs not + // relocatable by making assumptions on where the docs + // are hosted relative to the site root. + report.errors += 1; + println!( + "{}:{}: absolute path - {}", + pretty_path, + i, + Path::new(base).join(url).display() + ); + return; + } + Component::CurDir => {} + Component::ParentDir => { + path.pop(); + } + Component::Normal(s) => { + path.push(s); + } + } + } + } + + let (target_pretty_path, target_entry) = self.load_file(&path, report); + let (target_source, target_ids) = match target_entry { + FileEntry::Missing => { + if is_exception(file, &target_pretty_path) { + report.links_ignored_exception += 1; + } else { + report.errors += 1; + println!("{}:{}: broken link - `{}`", pretty_path, i, target_pretty_path); + } + return; + } + FileEntry::Dir => { + // Links to directories show as directory listings when viewing + // the docs offline so it's best to avoid them. + report.errors += 1; + println!( + "{}:{}: directory link to `{}` \ + (directory links should use index.html instead)", + pretty_path, i, target_pretty_path + ); + return; + } + FileEntry::OtherFile => return, + FileEntry::Redirect { target } => { + let t = target.clone(); + let (target, redir_entry) = self.load_file(&t, report); + match redir_entry { + FileEntry::Missing => { + report.errors += 1; + println!( + "{}:{}: broken redirect from `{}` to `{}`", + pretty_path, i, target_pretty_path, target + ); + return; + } + FileEntry::Redirect { target } => { + // Redirect to a redirect, this link checker + // currently doesn't support this, since it would + // require cycle checking, etc. + report.errors += 1; + println!( + "{}:{}: redirect from `{}` to `{}` \ + which is also a redirect (not supported)", + pretty_path, + i, + target_pretty_path, + target.display() + ); + return; + } + FileEntry::Dir => { + report.errors += 1; + println!( + "{}:{}: redirect from `{}` to `{}` \ + which is a directory \ + (directory links should use index.html instead)", + pretty_path, i, target_pretty_path, target + ); + return; + } + FileEntry::OtherFile => return, + FileEntry::HtmlFile { source, ids } => (source, ids), + } + } + FileEntry::HtmlFile { source, ids } => (source, ids), + }; + + // Alright, if we've found an HTML file for the target link. If + // this is a fragment link, also check that the `id` exists. + if let Some(ref fragment) = fragment { + // Fragments like `#1-6` are most likely line numbers to be + // interpreted by javascript, so we're ignoring these + if fragment.splitn(2, '-').all(|f| f.chars().all(|c| c.is_numeric())) { + return; + } + + parse_ids(&mut target_ids.borrow_mut(), &pretty_path, target_source, report); + + if target_ids.borrow().contains(*fragment) { + return; + } + + if is_exception(file, &format!("#{}", fragment)) { + report.links_ignored_exception += 1; + } else { + report.errors += 1; + print!("{}:{}: broken link fragment ", pretty_path, i); + println!("`#{}` pointing to `{}`", fragment, target_pretty_path); + }; + } + } + fn check_intra_doc_links( &mut self, file: &Path, @@ -496,59 +499,93 @@ fn maybe_redirect(source: &str) -> Option { find_redirect(REDIRECT_RUSTDOC).or_else(|| find_redirect(REDIRECT_MDBOOK)) } -fn with_attrs_in_source(source: &str, attr: &str, mut f: F) { - let mut base = ""; - for (i, mut line) in source.lines().enumerate() { - while let Some(j) = line.find(attr) { - let rest = &line[j + attr.len()..]; - // The base tag should always be the first link in the document so - // we can get away with using one pass. - let is_base = line[..j].ends_with("(source: &str, sink: Sink) -> Sink { + let tendril: ByteTendril = source.as_bytes().into(); + let mut input = BufferQueue::new(); + input.push_back(tendril.try_reinterpret().unwrap()); - let rest = &rest[pos_equals + 1..]; + let mut tok = Tokenizer::new(sink, TokenizerOpts::default()); + let _ = tok.feed(&mut input); + assert!(input.is_empty()); + tok.end(); + tok.sink +} - let pos_quote = match rest.find(&['"', '\''][..]) { - Some(i) => i, - None => continue, - }; - let quote_delim = rest.as_bytes()[pos_quote] as char; +#[derive(Default)] +struct AttrCollector { + attr_name: &'static [u8], + base: Option, + found_attrs: Vec<(u64, String)>, + /// Tracks whether or not it is inside a