diff --git a/Cargo.lock b/Cargo.lock index cafc623c185a6..1c1607e4c1ac4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -228,19 +228,13 @@ dependencies = [ "backtrace", ] -[[package]] -name = "ar" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d67af77d68a931ecd5cbd8a3b5987d63a1d1d1278f7f6a60ae33db485cdebb69" - [[package]] name = "ar_archive_writer" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" +checksum = "f8412a2d690663356cba5a2532f3ed55d1e8090743bc6695b88403b27df67733" dependencies = [ - "object 0.32.2", + "object 0.35.0", ] [[package]] @@ -1618,6 +1612,17 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "gimli" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + [[package]] name = "glob" version = "0.3.1" @@ -1671,6 +1676,7 @@ dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", "rustc-std-workspace-core", + "serde", ] [[package]] @@ -1880,6 +1886,12 @@ dependencies = [ "syn 2.0.67", ] +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + [[package]] name = "ident_case" version = "1.0.1" @@ -2109,6 +2121,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" +[[package]] +name = "lexopt" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401" + [[package]] name = "libc" version = "0.2.155" @@ -2623,7 +2641,7 @@ dependencies = [ "indexmap", "memchr", "ruzstd 0.5.0", - "wasmparser", + "wasmparser 0.118.2", ] [[package]] @@ -2637,6 +2655,15 @@ dependencies = [ "ruzstd 0.6.0", ] +[[package]] +name = "object" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +dependencies = [ + "memchr", +] + [[package]] name = "object" version = "0.36.0" @@ -3418,14 +3445,13 @@ dependencies = [ name = "run_make_support" version = "0.2.0" dependencies = [ - "ar", "bstr", "build_helper", - "gimli 0.28.1", + "gimli 0.31.0", "object 0.34.0", "regex", "similar", - "wasmparser", + "wasmparser 0.118.2", ] [[package]] @@ -3812,7 +3838,7 @@ dependencies = [ "thin-vec", "thorin-dwp", "tracing", - "wasm-encoder", + "wasm-encoder 0.200.0", "windows", ] @@ -4385,6 +4411,7 @@ dependencies = [ name = "rustc_mir_build" version = "0.0.0" dependencies = [ + "either", "itertools", "rustc_apfloat", "rustc_arena", @@ -5256,6 +5283,15 @@ dependencies = [ "color-eyre", ] +[[package]] +name = "spdx" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47317bbaf63785b53861e1ae2d11b80d6b624211d42cb20efcd210ee6f8a14bc" +dependencies = [ + "smallvec", +] + [[package]] name = "spdx-expression" version = "0.5.2" @@ -6304,6 +6340,28 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-component-ld" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "314d932d5e84c9678751b85498b1482b2f32f185744e449d3ce0b1d400376dad" +dependencies = [ + "anyhow", + "clap", + "lexopt", + "tempfile", + "wasmparser 0.210.0", + "wat", + "wit-component", +] + +[[package]] +name = "wasm-component-ld-wrapper" +version = "0.1.0" +dependencies = [ + "wasm-component-ld", +] + [[package]] name = "wasm-encoder" version = "0.200.0" @@ -6313,6 +6371,40 @@ dependencies = [ "leb128", ] +[[package]] +name = "wasm-encoder" +version = "0.210.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e3764d9d6edabd8c9e16195e177be0d20f6ab942ad18af52860f12f82bc59a" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.211.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e7d931a1120ef357f32b74547646b6fa68ea25e377772b72874b131a9ed70d4" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-metadata" +version = "0.210.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "012729d1294907fcb0866f08460ab95426a6d0b176a599619b84cac7653452b4" +dependencies = [ + "anyhow", + "indexmap", + "serde", + "serde_derive", + "serde_json", + "spdx", + "wasm-encoder 0.210.0", + "wasmparser 0.210.0", +] + [[package]] name = "wasmparser" version = "0.118.2" @@ -6323,6 +6415,42 @@ dependencies = [ "semver", ] +[[package]] +name = "wasmparser" +version = "0.210.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7bbcd21e7581619d9f6ca00f8c4f08f1cacfe58bf63f83af57cd0476f1026f5" +dependencies = [ + "ahash", + "bitflags 2.5.0", + "hashbrown", + "indexmap", + "semver", + "serde", +] + +[[package]] +name = "wast" +version = "211.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b25506dd82d00da6b14a87436b3d52b1d264083fa79cdb72a0d1b04a8595ccaa" +dependencies = [ + "bumpalo", + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.211.1", +] + +[[package]] +name = "wat" +version = "1.211.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb716ca6c86eecac2d82541ffc39860118fc0af9309c4f2670637bea2e1bdd7d" +dependencies = [ + "wast", +] + [[package]] name = "winapi" version = "0.3.9" @@ -6550,6 +6678,43 @@ dependencies = [ "memchr", ] +[[package]] +name = "wit-component" +version = "0.210.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a450bdb5d032acf1fa0865451fa0c6f50e62f2d31eaa8dba967c2e2d068694a4" +dependencies = [ + "anyhow", + "bitflags 2.5.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.210.0", + "wasm-metadata", + "wasmparser 0.210.0", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.210.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a965cbd439af19a4b44a54a97ab8957d86f02d01320efc9e31c1d3605c6710" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.210.0", +] + [[package]] name = "writeable" version = "0.5.5" diff --git a/Cargo.toml b/Cargo.toml index 93c520b0d689d..ce87a8c20b77d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ members = [ "src/tools/opt-dist", "src/tools/coverage-dump", "src/tools/rustc-perf-wrapper", + "src/tools/wasm-component-ld", ] exclude = [ @@ -104,6 +105,9 @@ rustc-demangle.debug = 0 [profile.release.package.lld-wrapper] debug = 0 strip = true +[profile.release.package.wasm-component-ld-wrapper] +debug = 0 +strip = true [patch.crates-io] # See comments in `library/rustc-std-workspace-core/README.md` for what's going on diff --git a/RELEASES.md b/RELEASES.md index 9e658568dc9a1..0ecd472efb6e2 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -67,7 +67,7 @@ Stabilized APIs - [`NonNull::byte_add`](https://doc.rust-lang.org/beta/std/ptr/struct.NonNull.html#method.byte_add) - [`NonNull::sub`](https://doc.rust-lang.org/beta/std/ptr/struct.NonNull.html#method.sub) - [`NonNull::byte_sub`](https://doc.rust-lang.org/beta/std/ptr/struct.NonNull.html#method.byte_sub) -- [`NonNull:offset_from`](https://doc.rust-lang.org/beta/std/ptr/struct.NonNull.html#method.offset_from) +- [`NonNull::offset_from`](https://doc.rust-lang.org/beta/std/ptr/struct.NonNull.html#method.offset_from) - [`NonNull::byte_offset_from`](https://doc.rust-lang.org/beta/std/ptr/struct.NonNull.html#method.byte_offset_from) - [`NonNull::read`](https://doc.rust-lang.org/beta/std/ptr/struct.NonNull.html#method.read) - [`NonNull::read_volatile`](https://doc.rust-lang.org/beta/std/ptr/struct.NonNull.html#method.read_volatile) @@ -91,9 +91,9 @@ Stabilized APIs - [`str::trim_ascii`](https://doc.rust-lang.org/beta/std/primitive.str.html#method.trim_ascii) - [`str::trim_ascii_start`](https://doc.rust-lang.org/beta/std/primitive.str.html#method.trim_ascii_start) - [`str::trim_ascii_end`](https://doc.rust-lang.org/beta/std/primitive.str.html#method.trim_ascii_end) -- [`<[AsciiChar]>::trim_ascii`](https://doc.rust-lang.org/beta/core/primitive.slice.html#method.trim_ascii) -- [`<[AsciiChar]>::trim_ascii_start`](https://doc.rust-lang.org/beta/core/primitive.slice.html#method.trim_ascii_start) -- [`<[AsciiChar]>::trim_ascii_end`](https://doc.rust-lang.org/beta/core/primitive.slice.html#method.trim_ascii_end) +- [`<[u8]>::trim_ascii`](https://doc.rust-lang.org/beta/core/primitive.slice.html#method.trim_ascii) +- [`<[u8]>::trim_ascii_start`](https://doc.rust-lang.org/beta/core/primitive.slice.html#method.trim_ascii_start) +- [`<[u8]>::trim_ascii_end`](https://doc.rust-lang.org/beta/core/primitive.slice.html#method.trim_ascii_end) - [`Ipv4Addr::BITS`](https://doc.rust-lang.org/beta/core/net/struct.Ipv4Addr.html#associatedconstant.BITS) - [`Ipv4Addr::to_bits`](https://doc.rust-lang.org/beta/core/net/struct.Ipv4Addr.html#method.to_bits) - [`Ipv4Addr::from_bits`](https://doc.rust-lang.org/beta/core/net/struct.Ipv4Addr.html#method.from_bits) diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index 3cb56a7d3121c..5008069542f1f 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -27,7 +27,7 @@ features = ['unprefixed_malloc_on_supported_platforms'] [features] # tidy-alphabetical-start -jemalloc = ['jemalloc-sys'] +jemalloc = ['dep:jemalloc-sys'] llvm = ['rustc_driver_impl/llvm'] max_level_info = ['rustc_driver_impl/max_level_info'] rustc_use_parallel_compiler = ['rustc_driver_impl/rustc_use_parallel_compiler'] diff --git a/compiler/rustc/src/main.rs b/compiler/rustc/src/main.rs index 7ba58406ef1ad..29766fc9d87cb 100644 --- a/compiler/rustc/src/main.rs +++ b/compiler/rustc/src/main.rs @@ -34,7 +34,7 @@ fn main() { // See the comment at the top of this file for an explanation of this. - #[cfg(feature = "jemalloc-sys")] + #[cfg(feature = "jemalloc")] { use std::os::raw::{c_int, c_void}; diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml index 5031e7a6705f0..7448f066d0ac1 100644 --- a/compiler/rustc_abi/Cargo.toml +++ b/compiler/rustc_abi/Cargo.toml @@ -21,10 +21,10 @@ default = ["nightly", "randomize"] # rust-analyzer depends on this crate and we therefore require it to built on a stable toolchain # without depending on rustc_data_structures, rustc_macros and rustc_serialize nightly = [ - "rustc_data_structures", + "dep:rustc_data_structures", + "dep:rustc_macros", + "dep:rustc_serialize", "rustc_index/nightly", - "rustc_macros", - "rustc_serialize", ] -randomize = ["rand", "rand_xoshiro", "nightly"] +randomize = ["dep:rand", "dep:rand_xoshiro", "nightly"] # tidy-alphabetical-end diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 78332d66f0366..52ec41f643c0d 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -627,7 +627,7 @@ impl Step for Size { #[inline] unsafe fn forward_unchecked(start: Self, count: usize) -> Self { - Self::from_bytes(u64::forward_unchecked(start.bytes(), count)) + Self::from_bytes(unsafe { u64::forward_unchecked(start.bytes(), count) }) } #[inline] @@ -642,7 +642,7 @@ impl Step for Size { #[inline] unsafe fn backward_unchecked(start: Self, count: usize) -> Self { - Self::from_bytes(u64::backward_unchecked(start.bytes(), count)) + Self::from_bytes(unsafe { u64::backward_unchecked(start.bytes(), count) }) } } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 1c1163551db5f..39d0f2c7305f9 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -482,7 +482,7 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { TyKind::Slice(ty) => vis.visit_ty(ty), TyKind::Ptr(mt) => vis.visit_mt(mt), TyKind::Ref(lt, mt) => { - visit_opt(lt, |lt| noop_visit_lifetime(lt, vis)); + visit_opt(lt, |lt| vis.visit_lifetime(lt)); vis.visit_mt(mt); } TyKind::BareFn(bft) => { @@ -925,7 +925,7 @@ pub fn noop_flat_map_generic_param( vis.visit_id(id); visit_attrs(attrs, vis); vis.visit_ident(ident); - visit_vec(bounds, |bound| noop_visit_param_bound(bound, vis)); + visit_vec(bounds, |bound| vis.visit_param_bound(bound)); match kind { GenericParamKind::Lifetime => {} GenericParamKind::Type { default } => { @@ -983,7 +983,7 @@ fn noop_visit_where_predicate(pred: &mut WherePredicate, vis: &mu } WherePredicate::RegionPredicate(rp) => { let WhereRegionPredicate { span, lifetime, bounds } = rp; - noop_visit_lifetime(lifetime, vis); + vis.visit_lifetime(lifetime); visit_vec(bounds, |bound| noop_visit_param_bound(bound, vis)); vis.visit_span(span); } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index efe1956615216..9478da236c315 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -699,8 +699,7 @@ impl Token { false } - /// Would `maybe_whole_expr` in `parser.rs` return `Ok(..)`? - /// That is, is this a pre-parsed expression dropped into the token stream + /// Is this a pre-parsed expression dropped into the token stream /// (which happens while parsing the result of macro expansion)? pub fn is_whole_expr(&self) -> bool { if let Interpolated(nt) = &self.kind diff --git a/compiler/rustc_ast_ir/Cargo.toml b/compiler/rustc_ast_ir/Cargo.toml index a78c91e0615ba..1905574073f13 100644 --- a/compiler/rustc_ast_ir/Cargo.toml +++ b/compiler/rustc_ast_ir/Cargo.toml @@ -14,8 +14,8 @@ rustc_span = { path = "../rustc_span", optional = true } [features] default = ["nightly"] nightly = [ - "rustc_serialize", - "rustc_data_structures", - "rustc_macros", - "rustc_span", + "dep:rustc_serialize", + "dep:rustc_data_structures", + "dep:rustc_macros", + "dep:rustc_span", ] diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 666e7763e6276..de0874af934cf 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -187,7 +187,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .emit(); } hir::InlineAsmOperand::Const { - anon_const: self.lower_anon_const(anon_const), + anon_const: self.lower_anon_const_to_anon_const(anon_const), } } InlineAsmOperand::Sym { sym } => { @@ -222,18 +222,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; // Wrap the expression in an AnonConst. - let parent_def_id = self.current_hir_id_owner; + let parent_def_id = self.current_def_id_parent; let node_id = self.next_node_id(); - self.create_def( - parent_def_id.def_id, - node_id, - kw::Empty, - DefKind::AnonConst, - *op_sp, - ); + // HACK(min_generic_const_args): see lower_anon_const + if !expr.is_potential_trivial_const_arg() { + self.create_def( + parent_def_id, + node_id, + kw::Empty, + DefKind::AnonConst, + *op_sp, + ); + } let anon_const = AnonConst { id: node_id, value: P(expr) }; hir::InlineAsmOperand::SymFn { - anon_const: self.lower_anon_const(&anon_const), + anon_const: self.lower_anon_const_to_anon_const(&anon_const), } } } diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 678cac210f413..6df2c15ce60c0 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -38,7 +38,7 @@ use crate::{ImplTraitPosition, ResolverAstLoweringExt}; -use super::{ImplTraitContext, LoweringContext, ParamMode}; +use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs}; use ast::visit::Visitor; use hir::def::{DefKind, PartialRes, Res}; @@ -259,8 +259,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self_param_id: pat_node_id, }; self_resolver.visit_block(block); - let block = this.lower_block(block, false); - this.mk_expr(hir::ExprKind::Block(block, None), block.span) + this.lower_target_expr(&block) } else { let pat_hir_id = this.lower_node_id(pat_node_id); this.generate_arg(pat_hir_id, span) @@ -273,26 +272,81 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } - // Generates fully qualified call for the resulting body. + // FIXME(fn_delegation): Alternatives for target expression lowering: + // https://github.com/rust-lang/rfcs/pull/3530#issuecomment-2197170600. + fn lower_target_expr(&mut self, block: &Block) -> hir::Expr<'hir> { + if block.stmts.len() == 1 + && let StmtKind::Expr(expr) = &block.stmts[0].kind + { + return self.lower_expr_mut(expr); + } + + let block = self.lower_block(block, false); + self.mk_expr(hir::ExprKind::Block(block, None), block.span) + } + + // Generates expression for the resulting body. If possible, `MethodCall` is used + // to allow autoref/autoderef for target expression. For example in: + // + // trait Trait : Sized { + // fn by_value(self) -> i32 { 1 } + // fn by_mut_ref(&mut self) -> i32 { 2 } + // fn by_ref(&self) -> i32 { 3 } + // } + // + // struct NewType(SomeType); + // impl Trait for NewType { + // reuse Trait::* { self.0 } + // } + // + // `self.0` will automatically coerce. fn finalize_body_lowering( &mut self, delegation: &Delegation, args: Vec>, span: Span, ) -> hir::Expr<'hir> { - let path = self.lower_qpath( - delegation.id, - &delegation.qself, - &delegation.path, - ParamMode::Optional, - ImplTraitContext::Disallowed(ImplTraitPosition::Path), - None, - ); - let args = self.arena.alloc_from_iter(args); - let path_expr = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span)); - let call = self.arena.alloc(self.mk_expr(hir::ExprKind::Call(path_expr, args), span)); + let has_generic_args = + delegation.path.segments.iter().rev().skip(1).any(|segment| segment.args.is_some()); + + let call = if self + .get_resolution_id(delegation.id, span) + .and_then(|def_id| Ok(self.has_self(def_id, span))) + .unwrap_or_default() + && delegation.qself.is_none() + && !has_generic_args + { + let ast_segment = delegation.path.segments.last().unwrap(); + let segment = self.lower_path_segment( + delegation.path.span, + ast_segment, + ParamMode::Optional, + ParenthesizedGenericArgs::Err, + ImplTraitContext::Disallowed(ImplTraitPosition::Path), + None, + ); + let segment = self.arena.alloc(segment); + + self.arena.alloc(hir::Expr { + hir_id: self.next_id(), + kind: hir::ExprKind::MethodCall(segment, &args[0], &args[1..], span), + span, + }) + } else { + let path = self.lower_qpath( + delegation.id, + &delegation.qself, + &delegation.path, + ParamMode::Optional, + ImplTraitContext::Disallowed(ImplTraitPosition::Path), + None, + ); + + let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span)); + self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span)) + }; let block = self.arena.alloc(hir::Block { stmts: &[], expr: Some(call), diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 218fa9740229d..d870f9fe0aef2 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -75,10 +75,15 @@ impl<'hir> LoweringContext<'_, 'hir> { let kind = match &e.kind { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), ExprKind::ConstBlock(c) => { - let c = self.with_new_scopes(c.value.span, |this| hir::ConstBlock { - def_id: this.local_def_id(c.id), - hir_id: this.lower_node_id(c.id), - body: this.lower_const_body(c.value.span, Some(&c.value)), + let c = self.with_new_scopes(c.value.span, |this| { + let def_id = this.local_def_id(c.id); + hir::ConstBlock { + def_id, + hir_id: this.lower_node_id(c.id), + body: this.with_def_id_parent(def_id, |this| { + this.lower_const_body(c.value.span, Some(&c.value)) + }), + } }); hir::ExprKind::ConstBlock(c) } @@ -377,17 +382,14 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut generic_args = ThinVec::new(); for (idx, arg) in args.into_iter().enumerate() { if legacy_args_idx.contains(&idx) { - let parent_def_id = self.current_hir_id_owner; + let parent_def_id = self.current_def_id_parent; let node_id = self.next_node_id(); - // Add a definition for the in-band const def. - self.create_def( - parent_def_id.def_id, - node_id, - kw::Empty, - DefKind::AnonConst, - f.span, - ); + // HACK(min_generic_const_args): see lower_anon_const + if !arg.is_potential_trivial_const_arg() { + // Add a definition for the in-band const def. + self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, f.span); + } let anon_const = AnonConst { id: node_id, value: arg }; generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const))); @@ -622,6 +624,7 @@ impl<'hir> LoweringContext<'_, 'hir> { coroutine_source: hir::CoroutineSource, body: impl FnOnce(&mut Self) -> hir::Expr<'hir>, ) -> hir::ExprKind<'hir> { + let closure_def_id = self.local_def_id(closure_node_id); let coroutine_kind = hir::CoroutineKind::Desugared(desugaring_kind, coroutine_source); // The `async` desugaring takes a resume argument and maintains a `task_context`, @@ -672,22 +675,24 @@ impl<'hir> LoweringContext<'_, 'hir> { lifetime_elision_allowed: false, }); - let body = self.lower_body(move |this| { - this.coroutine_kind = Some(coroutine_kind); + let body = self.with_def_id_parent(closure_def_id, move |this| { + this.lower_body(move |this| { + this.coroutine_kind = Some(coroutine_kind); - let old_ctx = this.task_context; - if task_context.is_some() { - this.task_context = task_context; - } - let res = body(this); - this.task_context = old_ctx; + let old_ctx = this.task_context; + if task_context.is_some() { + this.task_context = task_context; + } + let res = body(this); + this.task_context = old_ctx; - (params, res) + (params, res) + }) }); // `static |<_task_context?>| -> { }`: hir::ExprKind::Closure(self.arena.alloc(hir::Closure { - def_id: self.local_def_id(closure_node_id), + def_id: closure_def_id, binder: hir::ClosureBinder::Default, capture_clause, bound_generic_params: &[], @@ -966,27 +971,30 @@ impl<'hir> LoweringContext<'_, 'hir> { fn_decl_span: Span, fn_arg_span: Span, ) -> hir::ExprKind<'hir> { + let closure_def_id = self.local_def_id(closure_id); let (binder_clause, generic_params) = self.lower_closure_binder(binder); let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| { - let mut coroutine_kind = if this - .attrs - .get(&closure_hir_id.local_id) - .is_some_and(|attrs| attrs.iter().any(|attr| attr.has_name(sym::coroutine))) - { - Some(hir::CoroutineKind::Coroutine(Movability::Movable)) - } else { - None - }; - let body_id = this.lower_fn_body(decl, |this| { - this.coroutine_kind = coroutine_kind; - let e = this.lower_expr_mut(body); - coroutine_kind = this.coroutine_kind; - e - }); - let coroutine_option = - this.closure_movability_for_fn(decl, fn_decl_span, coroutine_kind, movability); - (body_id, coroutine_option) + this.with_def_id_parent(closure_def_id, move |this| { + let mut coroutine_kind = if this + .attrs + .get(&closure_hir_id.local_id) + .is_some_and(|attrs| attrs.iter().any(|attr| attr.has_name(sym::coroutine))) + { + Some(hir::CoroutineKind::Coroutine(Movability::Movable)) + } else { + None + }; + let body_id = this.lower_fn_body(decl, |this| { + this.coroutine_kind = coroutine_kind; + let e = this.lower_expr_mut(body); + coroutine_kind = this.coroutine_kind; + e + }); + let coroutine_option = + this.closure_movability_for_fn(decl, fn_decl_span, coroutine_kind, movability); + (body_id, coroutine_option) + }) }); let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params); @@ -994,7 +1002,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let fn_decl = self.lower_fn_decl(decl, closure_id, fn_decl_span, FnDeclKind::Closure, None); let c = self.arena.alloc(hir::Closure { - def_id: self.local_def_id(closure_id), + def_id: closure_def_id, binder: binder_clause, capture_clause, bound_generic_params, @@ -1066,6 +1074,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn_decl_span: Span, fn_arg_span: Span, ) -> hir::ExprKind<'hir> { + let closure_def_id = self.local_def_id(closure_id); let (binder_clause, generic_params) = self.lower_closure_binder(binder); assert_matches!( @@ -1075,27 +1084,29 @@ impl<'hir> LoweringContext<'_, 'hir> { ); let body = self.with_new_scopes(fn_decl_span, |this| { - let inner_decl = - FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) }; - - // Transform `async |x: u8| -> X { ... }` into - // `|x: u8| || -> X { ... }`. - let body_id = this.lower_body(|this| { - let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments( - &inner_decl, - |this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)), - fn_decl_span, - body.span, - coroutine_kind, - hir::CoroutineSource::Closure, - ); + this.with_def_id_parent(closure_def_id, |this| { + let inner_decl = + FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) }; + + // Transform `async |x: u8| -> X { ... }` into + // `|x: u8| || -> X { ... }`. + let body_id = this.lower_body(|this| { + let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments( + &inner_decl, + |this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)), + fn_decl_span, + body.span, + coroutine_kind, + hir::CoroutineSource::Closure, + ); - let hir_id = this.lower_node_id(coroutine_kind.closure_id()); - this.maybe_forward_track_caller(body.span, closure_hir_id, hir_id); + let hir_id = this.lower_node_id(coroutine_kind.closure_id()); + this.maybe_forward_track_caller(body.span, closure_hir_id, hir_id); - (parameters, expr) - }); - body_id + (parameters, expr) + }); + body_id + }) }); let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params); @@ -1106,7 +1117,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_fn_decl(&decl, closure_id, fn_decl_span, FnDeclKind::Closure, None); let c = self.arena.alloc(hir::Closure { - def_id: self.local_def_id(closure_id), + def_id: closure_def_id, binder: binder_clause, capture_clause, bound_generic_params, diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 44f37b5533a67..23729124e21a9 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -181,7 +181,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { intravisit::walk_generic_param(self, param); } - fn visit_const_param_default(&mut self, param: HirId, ct: &'hir AnonConst) { + fn visit_const_param_default(&mut self, param: HirId, ct: &'hir ConstArg<'hir>) { self.with_parent(param, |this| { intravisit::walk_const_param_default(this, ct); }) @@ -229,6 +229,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { } fn visit_anon_const(&mut self, constant: &'hir AnonConst) { + // FIXME: use real span? self.insert(DUMMY_SP, constant.hir_id, Node::AnonConst(constant)); self.with_parent(constant.hir_id, |this| { @@ -244,6 +245,15 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } + fn visit_const_arg(&mut self, const_arg: &'hir ConstArg<'hir>) { + // FIXME: use real span? + self.insert(DUMMY_SP, const_arg.hir_id, Node::ConstArg(const_arg)); + + self.with_parent(const_arg.hir_id, |this| { + intravisit::walk_const_arg(this, const_arg); + }); + } + fn visit_expr(&mut self, expr: &'hir Expr<'hir>) { self.insert(expr.span, expr.hir_id, Node::Expr(expr)); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 0ad23b5356690..f990b4ba69b3f 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -61,7 +61,10 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { for (def_id, info) in lctx.children { let owner = self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom); - debug_assert!(matches!(owner, hir::MaybeOwner::Phantom)); + debug_assert!( + matches!(owner, hir::MaybeOwner::Phantom), + "duplicate copy of {def_id:?} in lctx.children" + ); *owner = info; } } @@ -713,7 +716,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir_id, def_id: self.local_def_id(v.id), data: self.lower_variant_data(hir_id, &v.data), - disr_expr: v.disr_expr.as_ref().map(|e| self.lower_anon_const(e)), + disr_expr: v.disr_expr.as_ref().map(|e| self.lower_anon_const_to_anon_const(e)), ident: self.lower_ident(v.ident), span: self.lower_span(v.span), } @@ -1601,7 +1604,7 @@ impl<'hir> LoweringContext<'_, 'hir> { if let Some((span, hir_id, def_id)) = host_param_parts { let const_node_id = self.next_node_id(); - let anon_const = + let anon_const_did = self.create_def(def_id, const_node_id, kw::Empty, DefKind::AnonConst, span); let const_id = self.next_id(); @@ -1609,7 +1612,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let bool_id = self.next_id(); self.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id))); - self.children.push((anon_const, hir::MaybeOwner::NonOwner(const_id))); + self.children.push((anon_const_did, hir::MaybeOwner::NonOwner(const_id))); let const_body = self.lower_body(|this| { ( @@ -1624,6 +1627,17 @@ impl<'hir> LoweringContext<'_, 'hir> { ) }); + let default_ac = self.arena.alloc(hir::AnonConst { + def_id: anon_const_did, + hir_id: const_id, + body: const_body, + span, + }); + let default_ct = self.arena.alloc(hir::ConstArg { + hir_id: self.next_id(), + kind: hir::ConstArgKind::Anon(default_ac), + is_desugared_from_effects: false, + }); let param = hir::GenericParam { def_id, hir_id, @@ -1648,12 +1662,7 @@ impl<'hir> LoweringContext<'_, 'hir> { )), )), // FIXME(effects) we might not need a default. - default: Some(self.arena.alloc(hir::AnonConst { - def_id: anon_const, - hir_id: const_id, - body: const_body, - span, - })), + default: Some(default_ct), is_host_effect: true, synthetic: true, }, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 24748d2d00968..0f5f4d8023bd1 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -120,6 +120,18 @@ struct LoweringContext<'a, 'hir> { is_in_dyn_type: bool, current_hir_id_owner: hir::OwnerId, + /// Why do we need this in addition to [`Self::current_hir_id_owner`]? + /// + /// Currently (as of June 2024), anonymous constants are not HIR owners; however, + /// they do get their own DefIds. Some of these DefIds have to be created during + /// AST lowering, rather than def collection, because we can't tell until after + /// name resolution whether an anonymous constant will end up instead being a + /// [`hir::ConstArgKind::Path`]. However, to compute which generics are + /// available to an anonymous constant nested inside another, we need to make + /// sure that the parent is recorded as the parent anon const, not the enclosing + /// item. So we need to track parent defs differently from HIR owners, since they + /// will be finer-grained in the case of anon consts. + current_def_id_parent: LocalDefId, item_local_id_counter: hir::ItemLocalId, trait_map: ItemLocalMap>, @@ -162,6 +174,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { attrs: SortedMap::default(), children: Vec::default(), current_hir_id_owner: hir::CRATE_OWNER_ID, + current_def_id_parent: CRATE_DEF_ID, item_local_id_counter: hir::ItemLocalId::ZERO, node_id_to_local_id: Default::default(), trait_map: Default::default(), @@ -592,7 +605,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let _old = self.node_id_to_local_id.insert(owner, hir::ItemLocalId::ZERO); debug_assert_eq!(_old, None); - let item = f(self); + let item = self.with_def_id_parent(def_id, f); debug_assert_eq!(def_id, item.def_id().def_id); // `f` should have consumed all the elements in these vectors when constructing `item`. debug_assert!(self.impl_trait_defs.is_empty()); @@ -612,6 +625,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.children.push((def_id, hir::MaybeOwner::Owner(info))); } + fn with_def_id_parent(&mut self, parent: LocalDefId, f: impl FnOnce(&mut Self) -> T) -> T { + let current_def_id_parent = std::mem::replace(&mut self.current_def_id_parent, parent); + let result = f(self); + self.current_def_id_parent = current_def_id_parent; + result + } + /// Installs the remapping `remap` in scope while `f` is being executed. /// This causes references to the `LocalDefId` keys to be changed to /// refer to the values instead. @@ -806,7 +826,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { LifetimeRes::Fresh { param, kind, .. } => { // Late resolution delegates to us the creation of the `LocalDefId`. let _def_id = self.create_def( - self.current_hir_id_owner.def_id, + self.current_hir_id_owner.def_id, // FIXME: should this use self.current_def_id_parent? param, kw::UnderscoreLifetime, DefKind::LifetimeParam, @@ -1044,7 +1064,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { AssocItemConstraintKind::Equality { term } => { let term = match term { Term::Ty(ty) => self.lower_ty(ty, itctx).into(), - Term::Const(c) => self.lower_anon_const(c).into(), + Term::Const(c) => self.lower_anon_const_to_const_arg(c).into(), }; hir::AssocItemConstraintKind::Equality { term } } @@ -1150,42 +1170,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ty, ); - // Construct an AnonConst where the expr is the "ty"'s path. - - let parent_def_id = self.current_hir_id_owner; - let node_id = self.next_node_id(); - let span = self.lower_span(ty.span); - - // Add a definition for the in-band const def. - let def_id = self.create_def( - parent_def_id.def_id, - node_id, - kw::Empty, - DefKind::AnonConst, - span, - ); - - let path_expr = Expr { - id: ty.id, - kind: ExprKind::Path(None, path.clone()), - span, - attrs: AttrVec::new(), - tokens: None, - }; - - let ct = self.with_new_scopes(span, |this| { - self.arena.alloc(hir::AnonConst { - def_id, - hir_id: this.lower_node_id(node_id), - body: this - .lower_const_body(path_expr.span, Some(&path_expr)), - span, - }) - }); - return GenericArg::Const(ConstArg { - value: ct, - is_desugared_from_effects: false, - }); + let ct = + self.lower_const_path_to_const_arg(path, res, ty.id, ty.span); + return GenericArg::Const(ct); } } } @@ -1193,10 +1180,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } GenericArg::Type(self.lower_ty(ty, itctx)) } - ast::GenericArg::Const(ct) => GenericArg::Const(ConstArg { - value: self.lower_anon_const(ct), - is_desugared_from_effects: false, - }), + ast::GenericArg::Const(ct) => GenericArg::Const(self.lower_anon_const_to_const_arg(ct)), } } @@ -1355,7 +1339,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TyKind::Array(ty, length) => { hir::TyKind::Array(self.lower_ty(ty, itctx), self.lower_array_length(length)) } - TyKind::Typeof(expr) => hir::TyKind::Typeof(self.lower_anon_const(expr)), + TyKind::Typeof(expr) => hir::TyKind::Typeof(self.lower_anon_const_to_anon_const(expr)), TyKind::TraitObject(bounds, kind) => { let mut lifetime_bound = None; let (bounds, lifetime_bound) = self.with_dyn_type_scope(true, |this| { @@ -1429,7 +1413,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ); self.create_def( - self.current_hir_id_owner.def_id, + self.current_hir_id_owner.def_id, // FIXME: should this use self.current_def_id_parent? *def_node_id, ident.name, DefKind::TyParam, @@ -1637,7 +1621,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>], ) -> hir::TyKind<'hir> { let opaque_ty_def_id = self.create_def( - self.current_hir_id_owner.def_id, + self.current_hir_id_owner.def_id, // FIXME: should this use self.current_def_id_parent? opaque_ty_node_id, kw::Empty, DefKind::OpaqueTy, @@ -2222,7 +2206,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { false } }) - .map(|def| self.lower_anon_const(def)); + .map(|def| self.lower_anon_const_to_const_arg(def)); ( hir::ParamName::Plain(self.lower_ident(param.ident)), @@ -2360,19 +2344,153 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { "using `_` for array lengths is unstable", ) .stash(c.value.span, StashKey::UnderscoreForArrayLengths); - hir::ArrayLen::Body(self.lower_anon_const(c)) + hir::ArrayLen::Body(self.lower_anon_const_to_const_arg(c)) } } - _ => hir::ArrayLen::Body(self.lower_anon_const(c)), + _ => hir::ArrayLen::Body(self.lower_anon_const_to_const_arg(c)), + } + } + + #[instrument(level = "debug", skip(self))] + fn lower_const_path_to_const_arg( + &mut self, + path: &Path, + res: Res, + ty_id: NodeId, + span: Span, + ) -> &'hir hir::ConstArg<'hir> { + let ct_kind = match res { + Res::Def(DefKind::ConstParam, _) => { + let qpath = self.lower_qpath( + ty_id, + &None, + path, + ParamMode::Optional, + ImplTraitContext::Disallowed(ImplTraitPosition::Path), + None, + ); + hir::ConstArgKind::Path(qpath) + } + _ => { + // Construct an AnonConst where the expr is the "ty"'s path. + + let parent_def_id = self.current_def_id_parent; + let node_id = self.next_node_id(); + let span = self.lower_span(span); + + // Add a definition for the in-band const def. + let def_id = + self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, span); + let hir_id = self.lower_node_id(node_id); + + let path_expr = Expr { + id: ty_id, + kind: ExprKind::Path(None, path.clone()), + span, + attrs: AttrVec::new(), + tokens: None, + }; + + let ct = self.with_new_scopes(span, |this| { + self.arena.alloc(hir::AnonConst { + def_id, + hir_id, + body: this.with_def_id_parent(def_id, |this| { + this.lower_const_body(path_expr.span, Some(&path_expr)) + }), + span, + }) + }); + hir::ConstArgKind::Anon(ct) + } + }; + + self.arena.alloc(hir::ConstArg { + hir_id: self.next_id(), + kind: ct_kind, + is_desugared_from_effects: false, + }) + } + + /// See [`hir::ConstArg`] for when to use this function vs + /// [`Self::lower_anon_const_to_anon_const`]. + fn lower_anon_const_to_const_arg(&mut self, anon: &AnonConst) -> &'hir hir::ConstArg<'hir> { + self.arena.alloc(self.lower_anon_const_to_const_arg_direct(anon)) + } + + #[instrument(level = "debug", skip(self))] + fn lower_anon_const_to_const_arg_direct(&mut self, anon: &AnonConst) -> hir::ConstArg<'hir> { + // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments + // currently have to be wrapped in curly brackets, so it's necessary to special-case. + let expr = if let ExprKind::Block(block, _) = &anon.value.kind + && let [stmt] = block.stmts.as_slice() + && let StmtKind::Expr(expr) = &stmt.kind + && let ExprKind::Path(..) = &expr.kind + { + expr + } else { + &anon.value + }; + let maybe_res = + self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res()); + debug!("res={:?}", maybe_res); + // FIXME(min_generic_const_args): for now we only lower params to ConstArgKind::Path + if let Some(res) = maybe_res + && let Res::Def(DefKind::ConstParam, _) = res + && let ExprKind::Path(qself, path) = &expr.kind + { + let qpath = self.lower_qpath( + expr.id, + qself, + path, + ParamMode::Optional, + ImplTraitContext::Disallowed(ImplTraitPosition::Path), + None, + ); + + return ConstArg { + hir_id: self.next_id(), + kind: hir::ConstArgKind::Path(qpath), + is_desugared_from_effects: false, + }; + } + + let lowered_anon = self.lower_anon_const_to_anon_const(anon); + ConstArg { + hir_id: self.next_id(), + kind: hir::ConstArgKind::Anon(lowered_anon), + is_desugared_from_effects: false, } } - fn lower_anon_const(&mut self, c: &AnonConst) -> &'hir hir::AnonConst { - self.arena.alloc(self.with_new_scopes(c.value.span, |this| hir::AnonConst { - def_id: this.local_def_id(c.id), - hir_id: this.lower_node_id(c.id), - body: this.lower_const_body(c.value.span, Some(&c.value)), - span: this.lower_span(c.value.span), + /// See [`hir::ConstArg`] for when to use this function vs + /// [`Self::lower_anon_const_to_const_arg`]. + fn lower_anon_const_to_anon_const(&mut self, c: &AnonConst) -> &'hir hir::AnonConst { + if c.value.is_potential_trivial_const_arg() { + // HACK(min_generic_const_args): see DefCollector::visit_anon_const + // Over there, we guess if this is a bare param and only create a def if + // we think it's not. However we may can guess wrong (see there for example) + // in which case we have to create the def here. + self.create_def( + self.current_def_id_parent, + c.id, + kw::Empty, + DefKind::AnonConst, + c.value.span, + ); + } + + self.arena.alloc(self.with_new_scopes(c.value.span, |this| { + let def_id = this.local_def_id(c.id); + let hir_id = this.lower_node_id(c.id); + hir::AnonConst { + def_id, + hir_id, + body: this.with_def_id_parent(def_id, |this| { + this.lower_const_body(c.value.span, Some(&c.value)) + }), + span: this.lower_span(c.value.span), + } })) } diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 7da726ef40868..8f7dd77420709 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -159,9 +159,6 @@ ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation} .type = inherent impl for this type .only_trait = only trait implementations may be annotated with {$annotation} -ast_passes_invalid_label = - invalid label name `{$name}` - ast_passes_invalid_unnamed_field = unnamed fields are not allowed outside of structs or unions .label = unnamed field declared here @@ -176,9 +173,6 @@ ast_passes_item_invalid_safety = items outside of `unsafe extern {"{ }"}` cannot ast_passes_item_underscore = `{$kind}` items in this context need a name .label = `_` is not a valid name for this `{$kind}` item -ast_passes_keyword_lifetime = - lifetimes cannot use keyword names - ast_passes_match_arm_with_no_body = `match` arm with no body .suggestion = add a body after the pattern @@ -275,6 +269,9 @@ ast_passes_unsafe_negative_impl = negative impls cannot be unsafe .negative = negative because of this .unsafe = unsafe because of this +ast_passes_unsafe_static = + static items cannot be declared with `unsafe` safety qualifier outside of `extern` block + ast_passes_visibility_not_permitted = visibility qualifiers are not permitted here .enum_variant = enum variants and their fields always share the visibility of the enum they are in diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index dd0d904c52cc2..34aac6e447304 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -38,7 +38,7 @@ use std::mem; use std::ops::{Deref, DerefMut}; use thin_vec::thin_vec; -use crate::errors; +use crate::errors::{self, TildeConstReason}; /// Is `self` allowed semantically as the first parameter in an `FnDecl`? enum SelfSemantic { @@ -46,27 +46,12 @@ enum SelfSemantic { No, } -/// What is the context that prevents using `~const`? -// FIXME(effects): Consider getting rid of this in favor of `errors::TildeConstReason`, they're -// almost identical. This gets rid of an abstraction layer which might be considered bad. -enum DisallowTildeConstContext<'a> { - TraitObject, - Fn(FnKind<'a>), - Trait(Span), - TraitImpl(Span), - Impl(Span), - TraitAssocTy(Span), - TraitImplAssocTy(Span), - InherentAssocTy(Span), - Item, -} - -enum TraitOrTraitImpl<'a> { +enum TraitOrTraitImpl { Trait { span: Span, constness: Option }, - TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref: &'a TraitRef }, + TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref: Span }, } -impl<'a> TraitOrTraitImpl<'a> { +impl TraitOrTraitImpl { fn constness(&self) -> Option { match self { Self::Trait { constness: Some(span), .. } @@ -81,9 +66,9 @@ struct AstValidator<'a> { features: &'a Features, /// The span of the `extern` in an `extern { ... }` block, if any. - extern_mod: Option<&'a Item>, + extern_mod: Option, - outer_trait_or_trait_impl: Option>, + outer_trait_or_trait_impl: Option, has_proc_macro_decls: bool, @@ -92,7 +77,7 @@ struct AstValidator<'a> { /// e.g., `impl Iterator`. outer_impl_trait: Option, - disallow_tilde_const: Option>, + disallow_tilde_const: Option, /// Used to ban `impl Trait` in path projections like `::Item` /// or `Foo::Bar` @@ -115,7 +100,7 @@ impl<'a> AstValidator<'a> { trait_.map(|(constness, polarity, trait_ref)| TraitOrTraitImpl::TraitImpl { constness, polarity, - trait_ref, + trait_ref: trait_ref.path.span, }), ); f(self); @@ -145,7 +130,7 @@ impl<'a> AstValidator<'a> { fn with_tilde_const( &mut self, - disallowed: Option>, + disallowed: Option, f: impl FnOnce(&mut Self), ) { let old = mem::replace(&mut self.disallow_tilde_const, disallowed); @@ -224,7 +209,7 @@ impl<'a> AstValidator<'a> { } } TyKind::TraitObject(..) => self - .with_tilde_const(Some(DisallowTildeConstContext::TraitObject), |this| { + .with_tilde_const(Some(TildeConstReason::TraitObject), |this| { visit::walk_ty(this, t) }), TyKind::Path(qself, path) => { @@ -284,19 +269,6 @@ impl<'a> AstValidator<'a> { self.session.dcx() } - fn check_lifetime(&self, ident: Ident) { - let valid_names = [kw::UnderscoreLifetime, kw::StaticLifetime, kw::Empty]; - if !valid_names.contains(&ident.name) && ident.without_first_quote().is_reserved() { - self.dcx().emit_err(errors::KeywordLifetime { span: ident.span }); - } - } - - fn check_label(&self, ident: Ident) { - if ident.without_first_quote().is_reserved() { - self.dcx().emit_err(errors::InvalidLabel { span: ident.span, name: ident.name }); - } - } - fn visibility_not_permitted(&self, vis: &Visibility, note: errors::VisibilityNotPermittedNote) { if let VisibilityKind::Inherited = vis.kind { return; @@ -354,7 +326,7 @@ impl<'a> AstValidator<'a> { } } - fn check_trait_fn_not_const(&self, constness: Const, parent: &TraitOrTraitImpl<'a>) { + fn check_trait_fn_not_const(&self, constness: Const, parent: &TraitOrTraitImpl) { let Const::Yes(span) = constness else { return; }; @@ -367,7 +339,7 @@ impl<'a> AstValidator<'a> { .. } = parent { - Some(trait_ref.path.span.shrink_to_lo()) + Some(trait_ref.shrink_to_lo()) } else { None }; @@ -466,6 +438,11 @@ impl<'a> AstValidator<'a> { } } + /// This ensures that items can only be `unsafe` (or unmarked) outside of extern + /// blocks. + /// + /// This additionally ensures that within extern blocks, items can only be + /// `safe`/`unsafe` inside of a `unsafe`-adorned extern block. fn check_item_safety(&self, span: Span, safety: Safety) { match self.extern_mod_safety { Some(extern_safety) => { @@ -579,7 +556,7 @@ impl<'a> AstValidator<'a> { } fn current_extern_span(&self) -> Span { - self.session.source_map().guess_head_span(self.extern_mod.unwrap().span) + self.session.source_map().guess_head_span(self.extern_mod.unwrap()) } /// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`. @@ -923,16 +900,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.walk_ty(ty) } - fn visit_label(&mut self, label: &'a Label) { - self.check_label(label.ident); - visit::walk_label(self, label); - } - - fn visit_lifetime(&mut self, lifetime: &'a Lifetime, _: visit::LifetimeCtxt) { - self.check_lifetime(lifetime.ident); - visit::walk_lifetime(self, lifetime); - } - fn visit_field_def(&mut self, field: &'a FieldDef) { self.deny_unnamed_field(field); visit::walk_field_def(self, field) @@ -980,7 +947,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { this.visit_vis(&item.vis); this.visit_ident(item.ident); let disallowed = matches!(constness, Const::No) - .then(|| DisallowTildeConstContext::TraitImpl(item.span)); + .then(|| TildeConstReason::TraitImpl { span: item.span }); this.with_tilde_const(disallowed, |this| this.visit_generics(generics)); this.visit_trait_ref(t); this.visit_ty(self_ty); @@ -1035,7 +1002,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { this.visit_vis(&item.vis); this.visit_ident(item.ident); this.with_tilde_const( - Some(DisallowTildeConstContext::Impl(item.span)), + Some(TildeConstReason::Impl { span: item.span }), |this| this.visit_generics(generics), ); this.visit_ty(self_ty); @@ -1080,7 +1047,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } ItemKind::ForeignMod(ForeignMod { abi, safety, .. }) => { self.with_in_extern_mod(*safety, |this| { - let old_item = mem::replace(&mut this.extern_mod, Some(item)); + let old_item = mem::replace(&mut this.extern_mod, Some(item.span)); this.visibility_not_permitted( &item.vis, errors::VisibilityNotPermittedNote::IndividualForeignItems, @@ -1154,7 +1121,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { this.visit_ident(item.ident); let disallowed = is_const_trait .is_none() - .then(|| DisallowTildeConstContext::Trait(item.span)); + .then(|| TildeConstReason::Trait { span: item.span }); this.with_tilde_const(disallowed, |this| { this.visit_generics(generics); walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits) @@ -1215,6 +1182,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } ItemKind::Static(box StaticItem { expr, safety, .. }) => { self.check_item_safety(item.span, *safety); + if matches!(safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::UnsafeStatic { span: item.span }); + } if expr.is_none() { self.dcx().emit_err(errors::StaticWithoutBody { @@ -1371,13 +1341,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } - fn visit_generic_param(&mut self, param: &'a GenericParam) { - if let GenericParamKind::Lifetime { .. } = param.kind { - self.check_lifetime(param.ident); - } - visit::walk_generic_param(self, param); - } - fn visit_param_bound(&mut self, bound: &'a GenericBound, ctxt: BoundKind) { match bound { GenericBound::Trait(trait_ref, modifiers) => { @@ -1399,40 +1362,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.dcx().emit_err(errors::ConstBoundTraitObject { span: trait_ref.span }); } (_, BoundConstness::Maybe(span), BoundPolarity::Positive) - if let Some(reason) = &self.disallow_tilde_const => + if let Some(reason) = self.disallow_tilde_const => { - let reason = match reason { - DisallowTildeConstContext::Fn(FnKind::Closure(..)) => { - errors::TildeConstReason::Closure - } - DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => { - errors::TildeConstReason::Function { ident: ident.span } - } - &DisallowTildeConstContext::Trait(span) => { - errors::TildeConstReason::Trait { span } - } - &DisallowTildeConstContext::TraitImpl(span) => { - errors::TildeConstReason::TraitImpl { span } - } - &DisallowTildeConstContext::Impl(span) => { - // FIXME(effects): Consider providing a help message or even a structured - // suggestion for moving such bounds to the assoc const fns if available. - errors::TildeConstReason::Impl { span } - } - &DisallowTildeConstContext::TraitAssocTy(span) => { - errors::TildeConstReason::TraitAssocTy { span } - } - &DisallowTildeConstContext::TraitImplAssocTy(span) => { - errors::TildeConstReason::TraitImplAssocTy { span } - } - &DisallowTildeConstContext::InherentAssocTy(span) => { - errors::TildeConstReason::InherentAssocTy { span } - } - DisallowTildeConstContext::TraitObject => { - errors::TildeConstReason::TraitObject - } - DisallowTildeConstContext::Item => errors::TildeConstReason::Item, - }; self.dcx().emit_err(errors::TildeConstDisallowed { span, reason }); } ( @@ -1569,7 +1500,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .and_then(TraitOrTraitImpl::constness) .is_some(); - let disallowed = (!tilde_const_allowed).then(|| DisallowTildeConstContext::Fn(fk)); + let disallowed = (!tilde_const_allowed).then(|| match fk { + FnKind::Fn(_, ident, _, _, _, _) => TildeConstReason::Function { ident: ident.span }, + FnKind::Closure(_, _, _) => TildeConstReason::Closure, + }); self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk)); } @@ -1664,12 +1598,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> { AssocItemKind::Type(_) => { let disallowed = (!parent_is_const).then(|| match self.outer_trait_or_trait_impl { Some(TraitOrTraitImpl::Trait { .. }) => { - DisallowTildeConstContext::TraitAssocTy(item.span) + TildeConstReason::TraitAssocTy { span: item.span } } Some(TraitOrTraitImpl::TraitImpl { .. }) => { - DisallowTildeConstContext::TraitImplAssocTy(item.span) + TildeConstReason::TraitImplAssocTy { span: item.span } } - None => DisallowTildeConstContext::InherentAssocTy(item.span), + None => TildeConstReason::InherentAssocTy { span: item.span }, }); self.with_tilde_const(disallowed, |this| { this.with_in_trait_impl(None, |this| visit::walk_assoc_item(this, item, ctxt)) @@ -1852,7 +1786,7 @@ pub fn check_crate( outer_trait_or_trait_impl: None, has_proc_macro_decls: false, outer_impl_trait: None, - disallow_tilde_const: Some(DisallowTildeConstContext::Item), + disallow_tilde_const: Some(TildeConstReason::Item), is_impl_trait_banned: false, extern_mod_safety: None, lint_buffer: lints, diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index bfb9047645011..783bca6b6958d 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -9,21 +9,6 @@ use rustc_span::{symbol::Ident, Span, Symbol}; use crate::fluent_generated as fluent; -#[derive(Diagnostic)] -#[diag(ast_passes_keyword_lifetime)] -pub struct KeywordLifetime { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(ast_passes_invalid_label)] -pub struct InvalidLabel { - #[primary_span] - pub span: Span, - pub name: Symbol, -} - #[derive(Diagnostic)] #[diag(ast_passes_visibility_not_permitted, code = E0449)] pub struct VisibilityNotPermitted { @@ -239,6 +224,13 @@ pub struct InvalidSafetyOnBareFn { pub span: Span, } +#[derive(Diagnostic)] +#[diag(ast_passes_unsafe_static)] +pub struct UnsafeStatic { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_bound_in_context)] pub struct BoundInContext<'a> { @@ -612,7 +604,7 @@ pub struct TildeConstDisallowed { pub reason: TildeConstReason, } -#[derive(Subdiagnostic)] +#[derive(Subdiagnostic, Copy, Clone)] pub enum TildeConstReason { #[note(ast_passes_closure)] Closure, diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 8bf3e670ff228..4cd0d9cb294f1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -1,7 +1,7 @@ use rustc_errors::Diag; use rustc_hir::def_id::LocalDefId; +use rustc_infer::error_reporting::infer::nice_region_error::NiceRegionError; use rustc_infer::infer::canonical::Canonical; -use rustc_infer::infer::error_reporting::nice_region_error::NiceRegionError; use rustc_infer::infer::region_constraints::Constraint; use rustc_infer::infer::region_constraints::RegionConstraintData; use rustc_infer::infer::RegionVariableOrigin; diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 1cb74849017ab..f7e4bba371220 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -4,7 +4,7 @@ #![allow(rustc::untranslatable_diagnostic)] use either::Either; -use hir::ClosureKind; +use hir::{ClosureKind, Path}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan}; @@ -16,6 +16,7 @@ use rustc_hir::{CoroutineKind, CoroutineSource, LangItem}; use rustc_middle::bug; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::mir::tcx::PlaceTy; +use rustc_middle::mir::VarDebugInfoContents; use rustc_middle::mir::{ self, AggregateKind, BindingForm, BorrowKind, CallSource, ClearCrossCrate, ConstraintCategory, FakeBorrowKind, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind, @@ -204,9 +205,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { is_loop_move = true; } + let mut has_suggest_reborrow = false; if !seen_spans.contains(&move_span) { if !closure { - self.suggest_ref_or_clone(mpi, &mut err, &mut in_pattern, move_spans); + self.suggest_ref_or_clone( + mpi, + &mut err, + &mut in_pattern, + move_spans, + moved_place.as_ref(), + &mut has_suggest_reborrow, + ); } let msg_opt = CapturedMessageOpt { @@ -214,6 +223,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { is_loop_message, is_move_msg, is_loop_move, + has_suggest_reborrow, maybe_reinitialized_locations_is_empty: maybe_reinitialized_locations .is_empty(), }; @@ -258,17 +268,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) { if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { // We have a `&mut` ref, we need to reborrow on each iteration (#62112). - err.span_suggestion_verbose( - span.shrink_to_lo(), - format!( - "consider creating a fresh reborrow of {} here", - self.describe_place(moved_place) - .map(|n| format!("`{n}`")) - .unwrap_or_else(|| "the mutable reference".to_string()), - ), - "&mut *", - Applicability::MachineApplicable, - ); + self.suggest_reborrow(&mut err, span, moved_place); } } @@ -345,6 +345,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { err: &mut Diag<'infcx>, in_pattern: &mut bool, move_spans: UseSpans<'tcx>, + moved_place: PlaceRef<'tcx>, + has_suggest_reborrow: &mut bool, ) { let move_span = match move_spans { UseSpans::ClosureUse { capture_kind_span, .. } => capture_kind_span, @@ -434,20 +436,44 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { 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) { - (def_id.as_local(), args, 1) + (typeck.type_dependent_def_id(parent_expr.hir_id), args, 1) } else if let hir::Node::Expr(parent_expr) = parent && let hir::ExprKind::Call(call, args) = parent_expr.kind && let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind() { - (def_id.as_local(), args, 0) + (Some(*def_id), args, 0) } else { (None, &[][..], 0) }; + + // If the moved value is a mut reference, it is used in a + // generic function and it's type is a generic param, it can be + // reborrowed to avoid moving. + // for example: + // struct Y(u32); + // x's type is '& mut Y' and it is used in `fn generic(x: T) {}`. + if let Some(def_id) = def_id + && self.infcx.tcx.def_kind(def_id).is_fn_like() + && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id) + && let ty::Param(_) = + self.infcx.tcx.fn_sig(def_id).skip_binder().skip_binder().inputs() + [pos + offset] + .kind() + { + let place = &self.move_data.move_paths[mpi].place; + let ty = place.ty(self.body, self.infcx.tcx).ty; + if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { + *has_suggest_reborrow = true; + self.suggest_reborrow(err, expr.span, moved_place); + return; + } + } + let mut can_suggest_clone = true; if let Some(def_id) = def_id - && let node = self.infcx.tcx.hir_node_by_def_id(def_id) + && let Some(local_def_id) = def_id.as_local() + && let node = self.infcx.tcx.hir_node_by_def_id(local_def_id) && let Some(fn_sig) = node.fn_sig() && let Some(ident) = node.ident() && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id) @@ -546,7 +572,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { self.suggest_cloning(err, ty, expr, None, Some(move_spans)); } } - if let Some(pat) = finder.pat { + + self.suggest_ref_for_dbg_args(expr, place, move_span, err); + + // it's useless to suggest inserting `ref` when the span don't comes from local code + if let Some(pat) = finder.pat + && !move_span.is_dummy() + && !self.infcx.tcx.sess.source_map().is_imported(move_span) + { *in_pattern = true; let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())]; if let Some(pat) = finder.parent_pat { @@ -561,6 +594,78 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { } } + // for dbg!(x) which may take ownership, suggest dbg!(&x) instead + // but here we actually do not check whether the macro name is `dbg!` + // so that we may extend the scope a bit larger to cover more cases + fn suggest_ref_for_dbg_args( + &self, + body: &hir::Expr<'_>, + place: &Place<'tcx>, + move_span: Span, + err: &mut Diag<'infcx>, + ) { + let var_info = self.body.var_debug_info.iter().find(|info| match info.value { + VarDebugInfoContents::Place(ref p) => p == place, + _ => false, + }); + let arg_name = if let Some(var_info) = var_info { + var_info.name + } else { + return; + }; + struct MatchArgFinder { + expr_span: Span, + match_arg_span: Option, + arg_name: Symbol, + } + impl Visitor<'_> for MatchArgFinder { + fn visit_expr(&mut self, e: &hir::Expr<'_>) { + // dbg! is expanded into a match pattern, we need to find the right argument span + if let hir::ExprKind::Match(expr, ..) = &e.kind + && let hir::ExprKind::Path(hir::QPath::Resolved( + _, + path @ Path { segments: [seg], .. }, + )) = &expr.kind + && seg.ident.name == self.arg_name + && self.expr_span.source_callsite().contains(expr.span) + { + self.match_arg_span = Some(path.span); + } + hir::intravisit::walk_expr(self, e); + } + } + + let mut finder = MatchArgFinder { expr_span: move_span, match_arg_span: None, arg_name }; + finder.visit_expr(body); + if let Some(macro_arg_span) = finder.match_arg_span { + err.span_suggestion_verbose( + macro_arg_span.shrink_to_lo(), + "consider borrowing instead of transferring ownership", + "&", + Applicability::MachineApplicable, + ); + } + } + + pub fn suggest_reborrow( + &self, + err: &mut Diag<'infcx>, + span: Span, + moved_place: PlaceRef<'tcx>, + ) { + err.span_suggestion_verbose( + span.shrink_to_lo(), + format!( + "consider creating a fresh reborrow of {} here", + self.describe_place(moved_place) + .map(|n| format!("`{n}`")) + .unwrap_or_else(|| "the mutable reference".to_string()), + ), + "&mut *", + Applicability::MachineApplicable, + ); + } + fn report_use_of_uninitialized( &self, mpi: MovePathIndex, diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index b7fbb71a0cfa5..f97459d16bacf 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -768,10 +768,11 @@ struct CapturedMessageOpt { is_loop_message: bool, is_move_msg: bool, is_loop_move: bool, + has_suggest_reborrow: bool, maybe_reinitialized_locations_is_empty: bool, } -impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { /// Finds the spans associated to a move or copy of move_place at location. pub(super) fn move_spans( &self, @@ -997,7 +998,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn explain_captures( &mut self, - err: &mut Diag<'_>, + err: &mut Diag<'infcx>, span: Span, move_span: Span, move_spans: UseSpans<'tcx>, @@ -1009,6 +1010,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { is_loop_message, is_move_msg, is_loop_move, + has_suggest_reborrow, maybe_reinitialized_locations_is_empty, } = msg_opt; if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans { @@ -1182,18 +1184,15 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { if let ty::Ref(_, _, hir::Mutability::Mut) = moved_place.ty(self.body, self.infcx.tcx).ty.kind() { - // If we are in a loop this will be suggested later. - if !is_loop_move { - err.span_suggestion_verbose( + // Suggest `reborrow` in other place for following situations: + // 1. If we are in a loop this will be suggested later. + // 2. If the moved value is a mut reference, it is used in a + // generic function and the corresponding arg's type is generic param. + if !is_loop_move && !has_suggest_reborrow { + self.suggest_reborrow( + err, move_span.shrink_to_lo(), - format!( - "consider creating a fresh reborrow of {} here", - self.describe_place(moved_place.as_ref()) - .map(|n| format!("`{n}`")) - .unwrap_or_else(|| "the mutable reference".to_string()), - ), - "&mut *", - Applicability::MachineApplicable, + moved_place.as_ref(), ); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 4b6c1b29f285d..fcf23aa478555 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -554,6 +554,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { is_loop_message: false, is_move_msg: false, is_loop_move: false, + has_suggest_reborrow: false, maybe_reinitialized_locations_is_empty: true, }; if let Some(use_spans) = use_spans { diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 55147ee337fde..6cf797b4761bc 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -10,11 +10,11 @@ use rustc_hir::GenericBound::Trait; use rustc_hir::QPath::Resolved; use rustc_hir::WherePredicate::BoundPredicate; use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate}; -use rustc_infer::infer::error_reporting::nice_region_error::{ +use rustc_infer::error_reporting::infer::nice_region_error::{ self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params, HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, }; -use rustc_infer::infer::error_reporting::region::unexpected_hidden_region_diagnostic; +use rustc_infer::error_reporting::infer::region::unexpected_hidden_region_diagnostic; use rustc_infer::infer::{NllRegionVariableOrigin, RelateParamBound}; use rustc_middle::bug; use rustc_middle::hir::place::PlaceBase; diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index 02b9c2d48b11b..8da4d80badfec 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -309,14 +309,10 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { } impl<'bccx, 'tcx> TypeRelation> for NllTypeRelating<'_, 'bccx, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { self.type_checker.infcx.tcx } - fn tag(&self) -> &'static str { - "nll::subtype" - } - #[instrument(skip(self, info), level = "trace", ret)] fn relate_with_variance>>( &mut self, @@ -370,7 +366,7 @@ impl<'bccx, 'tcx> TypeRelation> for NllTypeRelating<'_, 'bccx, 'tcx // shouldn't ever fail. Instead, it unconditionally emits an // alias-relate goal. assert!(!self.type_checker.infcx.next_trait_solver()); - self.tcx().dcx().span_delayed_bug( + self.cx().dcx().span_delayed_bug( self.span(), "failure to relate an opaque to itself should result in an error later on", ); @@ -540,7 +536,7 @@ impl<'bccx, 'tcx> PredicateEmittingRelation> for NllTypeRelating &mut self, obligations: impl IntoIterator, ty::Predicate<'tcx>>>, ) { - let tcx = self.tcx(); + let tcx = self.cx(); let param_env = self.param_env(); self.register_goals( obligations.into_iter().map(|to_pred| Goal::new(tcx, param_env, to_pred)), @@ -559,7 +555,7 @@ impl<'bccx, 'tcx> PredicateEmittingRelation> for NllTypeRelating .into_iter() .map(|goal| { Obligation::new( - self.tcx(), + self.cx(), ObligationCause::dummy_with_span(self.span()), goal.param_env, goal.predicate, diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs index 58928815e8930..bffd5672b9b0c 100644 --- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs +++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs @@ -4,6 +4,7 @@ use crate::errors; use rustc_ast::attr::mk_attr; use rustc_ast::token; use rustc_ast::{self as ast, AttrItem, AttrStyle}; +use rustc_parse::parser::ForceCollect; use rustc_parse::{new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_session::parse::ParseSess; use rustc_span::FileName; @@ -17,13 +18,14 @@ pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) { )); let start_span = parser.token.span; - let AttrItem { unsafety, path, args, tokens: _ } = match parser.parse_attr_item(false) { - Ok(ai) => ai, - Err(err) => { - err.emit(); - continue; - } - }; + let AttrItem { unsafety, path, args, tokens: _ } = + match parser.parse_attr_item(ForceCollect::No) { + Ok(ai) => ai, + Err(err) => { + err.emit(); + continue; + } + }; let end_span = parser.token.span; if parser.token != token::Eof { psess.dcx().emit_err(errors::InvalidCrateAttr { span: start_span.to(end_span) }); diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs index 3f23e0d9e046b..1935005a08c54 100644 --- a/compiler/rustc_codegen_cranelift/src/archive.rs +++ b/compiler/rustc_codegen_cranelift/src/archive.rs @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; use rustc_codegen_ssa::back::archive::{ - get_native_object_symbols, ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, + ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, }; use rustc_session::Session; @@ -9,7 +9,7 @@ pub(crate) struct ArArchiveBuilderBuilder; impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box { - Box::new(ArArchiveBuilder::new(sess, get_native_object_symbols)) + Box::new(ArArchiveBuilder::new(sess, &DEFAULT_OBJECT_READER)) } fn create_dll_import_lib( diff --git a/compiler/rustc_codegen_gcc/.rustfmt.toml b/compiler/rustc_codegen_gcc/.rustfmt.toml index 2a35f0230c690..725aec25a0718 100644 --- a/compiler/rustc_codegen_gcc/.rustfmt.toml +++ b/compiler/rustc_codegen_gcc/.rustfmt.toml @@ -1 +1,3 @@ +version = "Two" use_small_heuristics = "Max" +merge_derives = false diff --git a/compiler/rustc_codegen_gcc/build_system/src/clone_gcc.rs b/compiler/rustc_codegen_gcc/build_system/src/clone_gcc.rs index aee46afaeb040..cbf590c0c321a 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/clone_gcc.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/clone_gcc.rs @@ -34,7 +34,7 @@ impl Args { "--out-path" => match args.next() { Some(path) if !path.is_empty() => out_path = Some(path), _ => { - return Err("Expected an argument after `--out-path`, found nothing".into()) + return Err("Expected an argument after `--out-path`, found nothing".into()); } }, "--help" => { diff --git a/compiler/rustc_codegen_gcc/build_system/src/config.rs b/compiler/rustc_codegen_gcc/build_system/src/config.rs index 965aedd8be891..bbb711c8428b6 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/config.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/config.rs @@ -54,7 +54,7 @@ impl ConfigFile { config.gcc_path = Some(value.as_str().to_string()) } ("gcc-path", _) => { - return failed_config_parsing(config_file, "Expected a string for `gcc-path`") + return failed_config_parsing(config_file, "Expected a string for `gcc-path`"); } ("download-gccjit", TomlValue::Boolean(value)) => { config.download_gccjit = Some(*value) @@ -63,7 +63,7 @@ impl ConfigFile { return failed_config_parsing( config_file, "Expected a boolean for `download-gccjit`", - ) + ); } _ => return failed_config_parsing(config_file, &format!("Unknown key `{}`", key)), } @@ -73,7 +73,7 @@ impl ConfigFile { return failed_config_parsing( config_file, "At least one of `gcc-path` or `download-gccjit` value must be set", - ) + ); } (Some(_), Some(true)) => { println!( @@ -144,7 +144,7 @@ impl ConfigInfo { _ => { return Err( "Expected a value after `--target-triple`, found nothing".to_string() - ) + ); } }, "--out-dir" => match args.next() { @@ -158,7 +158,7 @@ impl ConfigInfo { self.config_file = Some(arg.to_string()); } _ => { - return Err("Expected a value after `--config-file`, found nothing".to_string()) + return Err("Expected a value after `--config-file`, found nothing".to_string()); } }, "--release-sysroot" => self.sysroot_release_channel = true, @@ -169,7 +169,7 @@ impl ConfigInfo { self.cg_gcc_path = Some(arg.into()); } _ => { - return Err("Expected a value after `--cg_gcc-path`, found nothing".to_string()) + return Err("Expected a value after `--cg_gcc-path`, found nothing".to_string()); } }, "--use-backend" => match args.next() { @@ -277,7 +277,7 @@ impl ConfigInfo { self.gcc_path = match gcc_path { Some(path) => path, None => { - return Err(format!("missing `gcc-path` value from `{}`", config_file.display(),)) + return Err(format!("missing `gcc-path` value from `{}`", config_file.display(),)); } }; Ok(()) diff --git a/compiler/rustc_codegen_gcc/build_system/src/test.rs b/compiler/rustc_codegen_gcc/build_system/src/test.rs index 8d088a3aac318..06f28d13fb3ab 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/test.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/test.rs @@ -109,7 +109,7 @@ impl TestArg { test_arg.flags.extend_from_slice(&["--features".into(), feature]); } _ => { - return Err("Expected an argument after `--features`, found nothing".into()) + return Err("Expected an argument after `--features`, found nothing".into()); } }, "--use-system-gcc" => { @@ -458,11 +458,7 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result { .map_err(|error| format!("Failed to retrieve cargo path: {:?}", error)) .and_then(|cargo| { let cargo = cargo.trim().to_owned(); - if cargo.is_empty() { - Err(format!("`cargo` path is empty")) - } else { - Ok(cargo) - } + if cargo.is_empty() { Err(format!("`cargo` path is empty")) } else { Ok(cargo) } })?; let rustc = String::from_utf8( run_command_with_env(&[&"rustup", &toolchain, &"which", &"rustc"], rust_dir, Some(env))? @@ -471,11 +467,7 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result { .map_err(|error| format!("Failed to retrieve rustc path: {:?}", error)) .and_then(|rustc| { let rustc = rustc.trim().to_owned(); - if rustc.is_empty() { - Err(format!("`rustc` path is empty")) - } else { - Ok(rustc) - } + if rustc.is_empty() { Err(format!("`rustc` path is empty")) } else { Ok(rustc) } })?; let llvm_filecheck = match run_command_with_env( &[ diff --git a/compiler/rustc_codegen_gcc/build_system/src/utils.rs b/compiler/rustc_codegen_gcc/build_system/src/utils.rs index 3bba8df6c6504..e338d1b4992e8 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/utils.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/utils.rs @@ -175,11 +175,7 @@ pub fn cargo_install(to_install: &str) -> Result<(), String> { pub fn get_os_name() -> Result { let output = run_command(&[&"uname"], None)?; let name = std::str::from_utf8(&output.stdout).unwrap_or("").trim().to_string(); - if !name.is_empty() { - Ok(name) - } else { - Err("Failed to retrieve the OS name".to_string()) - } + if !name.is_empty() { Ok(name) } else { Err("Failed to retrieve the OS name".to_string()) } } #[derive(Default, PartialEq)] diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 166dd080cf209..0a99e7213be56 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -26,11 +26,7 @@ impl<'a, 'gcc, 'tcx> AbiBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { } else { false }; - if on_stack { - param.to_lvalue().get_address(None) - } else { - param.to_rvalue() - } + if on_stack { param.to_lvalue().get_address(None) } else { param.to_rvalue() } } } diff --git a/compiler/rustc_codegen_gcc/src/archive.rs b/compiler/rustc_codegen_gcc/src/archive.rs index 73ff0c37b6656..21676f5dbb6a3 100644 --- a/compiler/rustc_codegen_gcc/src/archive.rs +++ b/compiler/rustc_codegen_gcc/src/archive.rs @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; use rustc_codegen_ssa::back::archive::{ - get_native_object_symbols, ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, + ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, }; use rustc_session::Session; @@ -11,7 +11,7 @@ pub(crate) struct ArArchiveBuilderBuilder; impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box { - Box::new(ArArchiveBuilder::new(sess, get_native_object_symbols)) + Box::new(ArArchiveBuilder::new(sess, &DEFAULT_OBJECT_READER)) } fn create_dll_import_lib( diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index aa485846cd429..1da691252ab94 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -858,11 +858,7 @@ fn modifier_to_gcc( InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => modifier, InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => { - if modifier == Some('v') { - None - } else { - modifier - } + if modifier == Some('v') { None } else { modifier } } InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => { unreachable!("clobber-only") diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 307348f595dc9..b9e4bd79fe1e6 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1043,11 +1043,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let llty = place.layout.scalar_pair_element_gcc_type(self, i); let load = self.load(llty, llptr, align); scalar_load_metadata(self, load, scalar); - if scalar.is_bool() { - self.trunc(load, self.type_i1()) - } else { - load - } + if scalar.is_bool() { self.trunc(load, self.type_i1()) } else { load } }; OperandValue::Pair( @@ -1795,18 +1791,10 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX. let int_max = |signed: bool, int_width: u64| -> u128 { let shift_amount = 128 - int_width; - if signed { - i128::MAX as u128 >> shift_amount - } else { - u128::MAX >> shift_amount - } + if signed { i128::MAX as u128 >> shift_amount } else { u128::MAX >> shift_amount } }; let int_min = |signed: bool, int_width: u64| -> i128 { - if signed { - i128::MIN >> (128 - int_width) - } else { - 0 - } + if signed { i128::MIN >> (128 - int_width) } else { 0 } }; let compute_clamp_bounds_single = |signed: bool, int_width: u64| -> (u128, u128) { diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 19333689aaa9f..70f0dc37e39da 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -58,11 +58,7 @@ pub fn type_is_pointer(typ: Type<'_>) -> bool { impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn const_null(&self, typ: Type<'gcc>) -> RValue<'gcc> { - if type_is_pointer(typ) { - self.context.new_null(typ) - } else { - self.const_int(typ, 0) - } + if type_is_pointer(typ) { self.context.new_null(typ) } else { self.const_int(typ, 0) } } fn const_undef(&self, typ: Type<'gcc>) -> RValue<'gcc> { diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index ca37602973540..5969d9b914403 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -21,14 +21,16 @@ pub(crate) unsafe fn codegen( ) { let llcx = &*module_llvm.llcx; let llmod = module_llvm.llmod(); - let usize = match tcx.sess.target.pointer_width { - 16 => llvm::LLVMInt16TypeInContext(llcx), - 32 => llvm::LLVMInt32TypeInContext(llcx), - 64 => llvm::LLVMInt64TypeInContext(llcx), - tws => bug!("Unsupported target word size for int: {}", tws), + let usize = unsafe { + match tcx.sess.target.pointer_width { + 16 => llvm::LLVMInt16TypeInContext(llcx), + 32 => llvm::LLVMInt32TypeInContext(llcx), + 64 => llvm::LLVMInt64TypeInContext(llcx), + tws => bug!("Unsupported target word size for int: {}", tws), + } }; - let i8 = llvm::LLVMInt8TypeInContext(llcx); - let i8p = llvm::LLVMPointerTypeInContext(llcx, 0); + let i8 = unsafe { llvm::LLVMInt8TypeInContext(llcx) }; + let i8p = unsafe { llvm::LLVMPointerTypeInContext(llcx, 0) }; if kind == AllocatorKind::Default { for method in ALLOCATOR_METHODS { @@ -73,23 +75,25 @@ pub(crate) unsafe fn codegen( true, ); - // __rust_alloc_error_handler_should_panic - let name = OomStrategy::SYMBOL; - let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8); - if tcx.sess.default_hidden_visibility() { - llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden); - } - let val = tcx.sess.opts.unstable_opts.oom.should_panic(); - let llval = llvm::LLVMConstInt(i8, val as u64, False); - llvm::LLVMSetInitializer(ll_g, llval); - - let name = NO_ALLOC_SHIM_IS_UNSTABLE; - let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8); - if tcx.sess.default_hidden_visibility() { - llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden); + unsafe { + // __rust_alloc_error_handler_should_panic + let name = OomStrategy::SYMBOL; + let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8); + if tcx.sess.default_hidden_visibility() { + llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden); + } + let val = tcx.sess.opts.unstable_opts.oom.should_panic(); + let llval = llvm::LLVMConstInt(i8, val as u64, False); + llvm::LLVMSetInitializer(ll_g, llval); + + let name = NO_ALLOC_SHIM_IS_UNSTABLE; + let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8); + if tcx.sess.default_hidden_visibility() { + llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden); + } + let llval = llvm::LLVMConstInt(i8, 0, False); + llvm::LLVMSetInitializer(ll_g, llval); } - let llval = llvm::LLVMConstInt(i8, 0, False); - llvm::LLVMSetInitializer(ll_g, llval); if tcx.sess.opts.debuginfo != DebugInfo::None { let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod); diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index a354f3d353610..f46c6b1c49800 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -15,8 +15,8 @@ use crate::errors::{ use crate::llvm::archive_ro::{ArchiveRO, Child}; use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport}; use rustc_codegen_ssa::back::archive::{ - get_native_object_symbols, try_extract_macho_fat_archive, ArArchiveBuilder, - ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder, UnknownArchiveKind, + try_extract_macho_fat_archive, ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder, + ArchiveBuilderBuilder, ObjectReader, UnknownArchiveKind, DEFAULT_OBJECT_READER, }; use tracing::trace; @@ -115,7 +115,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { if true { Box::new(LlvmArchiveBuilder { sess, additions: Vec::new() }) } else { - Box::new(ArArchiveBuilder::new(sess, get_llvm_object_symbols)) + Box::new(ArArchiveBuilder::new(sess, &LLVM_OBJECT_READER)) } } @@ -291,59 +291,84 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { // The object crate doesn't know how to get symbols for LLVM bitcode and COFF bigobj files. // As such we need to use LLVM for them. + +static LLVM_OBJECT_READER: ObjectReader = ObjectReader { + get_symbols: get_llvm_object_symbols, + is_64_bit_object_file: llvm_is_64_bit_object_file, + is_ec_object_file: llvm_is_ec_object_file, + get_xcoff_member_alignment: DEFAULT_OBJECT_READER.get_xcoff_member_alignment, +}; + +fn should_use_llvm_reader(buf: &[u8]) -> bool { + let is_bitcode = unsafe { llvm::LLVMRustIsBitcode(buf.as_ptr(), buf.len()) }; + + // COFF bigobj file, msvc LTO file or import library. See + // https://github.com/llvm/llvm-project/blob/453f27bc9/llvm/lib/BinaryFormat/Magic.cpp#L38-L51 + let is_unsupported_windows_obj_file = buf.get(0..4) == Some(b"\0\0\xFF\xFF"); + + is_bitcode || is_unsupported_windows_obj_file +} + #[deny(unsafe_op_in_unsafe_fn)] fn get_llvm_object_symbols( buf: &[u8], f: &mut dyn FnMut(&[u8]) -> io::Result<()>, ) -> io::Result { - let is_bitcode = unsafe { llvm::LLVMRustIsBitcode(buf.as_ptr(), buf.len()) }; + if !should_use_llvm_reader(buf) { + return (DEFAULT_OBJECT_READER.get_symbols)(buf, f); + } - // COFF bigobj file, msvc LTO file or import library. See - // https://github.com/llvm/llvm-project/blob/453f27bc9/llvm/lib/BinaryFormat/Magic.cpp#L38-L51 - let is_unsupported_windows_obj_file = buf.get(0..4) == Some(b"\0\0\xFF\xFF"); + let mut state = Box::new(f); - if is_bitcode || is_unsupported_windows_obj_file { - let mut state = Box::new(f); - - let err = unsafe { - llvm::LLVMRustGetSymbols( - buf.as_ptr(), - buf.len(), - std::ptr::addr_of_mut!(*state) as *mut c_void, - callback, - error_callback, - ) - }; + let err = unsafe { + llvm::LLVMRustGetSymbols( + buf.as_ptr(), + buf.len(), + std::ptr::addr_of_mut!(*state) as *mut c_void, + callback, + error_callback, + ) + }; - if err.is_null() { - return Ok(true); - } else { - return Err(unsafe { *Box::from_raw(err as *mut io::Error) }); - } + if err.is_null() { + return Ok(true); + } else { + return Err(unsafe { *Box::from_raw(err as *mut io::Error) }); + } - unsafe extern "C" fn callback( - state: *mut c_void, - symbol_name: *const c_char, - ) -> *mut c_void { - let f = unsafe { &mut *(state as *mut &mut dyn FnMut(&[u8]) -> io::Result<()>) }; - match f(unsafe { CStr::from_ptr(symbol_name) }.to_bytes()) { - Ok(()) => std::ptr::null_mut(), - Err(err) => Box::into_raw(Box::new(err)) as *mut c_void, - } + unsafe extern "C" fn callback(state: *mut c_void, symbol_name: *const c_char) -> *mut c_void { + let f = unsafe { &mut *(state as *mut &mut dyn FnMut(&[u8]) -> io::Result<()>) }; + match f(unsafe { CStr::from_ptr(symbol_name) }.to_bytes()) { + Ok(()) => std::ptr::null_mut(), + Err(err) => Box::into_raw(Box::new(err)) as *mut c_void, } + } - unsafe extern "C" fn error_callback(error: *const c_char) -> *mut c_void { - let error = unsafe { CStr::from_ptr(error) }; - Box::into_raw(Box::new(io::Error::new( - io::ErrorKind::Other, - format!("LLVM error: {}", error.to_string_lossy()), - ))) as *mut c_void - } - } else { - get_native_object_symbols(buf, f) + unsafe extern "C" fn error_callback(error: *const c_char) -> *mut c_void { + let error = unsafe { CStr::from_ptr(error) }; + Box::into_raw(Box::new(io::Error::new( + io::ErrorKind::Other, + format!("LLVM error: {}", error.to_string_lossy()), + ))) as *mut c_void } } +fn llvm_is_64_bit_object_file(buf: &[u8]) -> bool { + if !should_use_llvm_reader(buf) { + return (DEFAULT_OBJECT_READER.is_64_bit_object_file)(buf); + } + + unsafe { llvm::LLVMRustIs64BitSymbolicFile(buf.as_ptr(), buf.len()) } +} + +fn llvm_is_ec_object_file(buf: &[u8]) -> bool { + if !should_use_llvm_reader(buf) { + return (DEFAULT_OBJECT_READER.is_ec_object_file)(buf); + } + + unsafe { llvm::LLVMRustIsECObject(buf.as_ptr(), buf.len()) } +} + impl<'a> LlvmArchiveBuilder<'a> { fn build_with_llvm(&mut self, output: &Path) -> io::Result { let kind = &*self.sess.target.archive_format; diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index aff3e3d707608..aef672631c813 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -727,7 +727,7 @@ pub unsafe fn optimize_thin_module( // into that context. One day, however, we may do this for upstream // crates but for locally codegened modules we may be able to reuse // that LLVM Context and Module. - let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); + let llcx = unsafe { llvm::LLVMRustContextCreate(cgcx.fewer_names) }; let llmod_raw = parse_module(llcx, module_name, thin_module.data(), dcx)? as *const _; let mut module = ModuleCodegen { module_llvm: ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) }, @@ -750,7 +750,9 @@ pub unsafe fn optimize_thin_module( { let _timer = cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name()); - if !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) { + if unsafe { + !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) + } { return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule)); } save_temp_bitcode(cgcx, &module, "thin-lto-after-rename"); @@ -760,7 +762,8 @@ pub unsafe fn optimize_thin_module( let _timer = cgcx .prof .generic_activity_with_arg("LLVM_thin_lto_resolve_weak", thin_module.name()); - if !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) { + if unsafe { !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) } + { return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule)); } save_temp_bitcode(cgcx, &module, "thin-lto-after-resolve"); @@ -770,7 +773,8 @@ pub unsafe fn optimize_thin_module( let _timer = cgcx .prof .generic_activity_with_arg("LLVM_thin_lto_internalize", thin_module.name()); - if !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) { + if unsafe { !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) } + { return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule)); } save_temp_bitcode(cgcx, &module, "thin-lto-after-internalize"); @@ -779,7 +783,9 @@ pub unsafe fn optimize_thin_module( { let _timer = cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name()); - if !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target) { + if unsafe { + !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target) + } { return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule)); } save_temp_bitcode(cgcx, &module, "thin-lto-after-import"); diff --git a/compiler/rustc_codegen_llvm/src/back/profiling.rs b/compiler/rustc_codegen_llvm/src/back/profiling.rs index 2741f7d848e79..2eee9f8c5a3ec 100644 --- a/compiler/rustc_codegen_llvm/src/back/profiling.rs +++ b/compiler/rustc_codegen_llvm/src/back/profiling.rs @@ -46,13 +46,15 @@ pub unsafe extern "C" fn selfprofile_before_pass_callback( pass_name: *const c_char, ir_name: *const c_char, ) { - let llvm_self_profiler = &mut *(llvm_self_profiler as *mut LlvmSelfProfiler<'_>); - let pass_name = CStr::from_ptr(pass_name).to_str().expect("valid UTF-8"); - let ir_name = CStr::from_ptr(ir_name).to_str().expect("valid UTF-8"); - llvm_self_profiler.before_pass_callback(pass_name, ir_name); + unsafe { + let llvm_self_profiler = &mut *(llvm_self_profiler as *mut LlvmSelfProfiler<'_>); + let pass_name = CStr::from_ptr(pass_name).to_str().expect("valid UTF-8"); + let ir_name = CStr::from_ptr(ir_name).to_str().expect("valid UTF-8"); + llvm_self_profiler.before_pass_callback(pass_name, ir_name); + } } pub unsafe extern "C" fn selfprofile_after_pass_callback(llvm_self_profiler: *mut c_void) { - let llvm_self_profiler = &mut *(llvm_self_profiler as *mut LlvmSelfProfiler<'_>); + let llvm_self_profiler = unsafe { &mut *(llvm_self_profiler as *mut LlvmSelfProfiler<'_>) }; llvm_self_profiler.after_pass_callback(); } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 2fda19bf0c914..ddd52e80edff1 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -428,9 +428,10 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void if user.is_null() { return; } - let (cgcx, dcx) = *(user as *const (&CodegenContext, DiagCtxtHandle<'_>)); + let (cgcx, dcx) = + unsafe { *(user as *const (&CodegenContext, DiagCtxtHandle<'_>)) }; - match llvm::diagnostic::Diagnostic::unpack(info) { + match unsafe { llvm::diagnostic::Diagnostic::unpack(info) } { llvm::diagnostic::InlineAsm(inline) => { report_inline_asm(cgcx, inline.message, inline.level, inline.cookie, inline.source); } @@ -454,14 +455,14 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void }); } llvm::diagnostic::PGO(diagnostic_ref) | llvm::diagnostic::Linker(diagnostic_ref) => { - let message = llvm::build_string(|s| { + let message = llvm::build_string(|s| unsafe { llvm::LLVMRustWriteDiagnosticInfoToString(diagnostic_ref, s) }) .expect("non-UTF8 diagnostic"); dcx.emit_warn(FromLlvmDiag { message }); } llvm::diagnostic::Unsupported(diagnostic_ref) => { - let message = llvm::build_string(|s| { + let message = llvm::build_string(|s| unsafe { llvm::LLVMRustWriteDiagnosticInfoToString(diagnostic_ref, s) }) .expect("non-UTF8 diagnostic"); @@ -564,37 +565,39 @@ pub(crate) unsafe fn llvm_optimize( let llvm_plugins = config.llvm_plugins.join(","); - let result = llvm::LLVMRustOptimize( - module.module_llvm.llmod(), - &*module.module_llvm.tm, - to_pass_builder_opt_level(opt_level), - opt_stage, - cgcx.opts.cg.linker_plugin_lto.enabled(), - config.no_prepopulate_passes, - config.verify_llvm_ir, - using_thin_buffers, - config.merge_functions, - unroll_loops, - config.vectorize_slp, - config.vectorize_loop, - config.no_builtins, - config.emit_lifetime_markers, - sanitizer_options.as_ref(), - pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), - pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), - config.instrument_coverage, - instr_profile_output_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), - config.instrument_gcov, - pgo_sample_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), - config.debug_info_for_profiling, - llvm_selfprofiler, - selfprofile_before_pass_callback, - selfprofile_after_pass_callback, - extra_passes.as_ptr().cast(), - extra_passes.len(), - llvm_plugins.as_ptr().cast(), - llvm_plugins.len(), - ); + let result = unsafe { + llvm::LLVMRustOptimize( + module.module_llvm.llmod(), + &*module.module_llvm.tm, + to_pass_builder_opt_level(opt_level), + opt_stage, + cgcx.opts.cg.linker_plugin_lto.enabled(), + config.no_prepopulate_passes, + config.verify_llvm_ir, + using_thin_buffers, + config.merge_functions, + unroll_loops, + config.vectorize_slp, + config.vectorize_loop, + config.no_builtins, + config.emit_lifetime_markers, + sanitizer_options.as_ref(), + pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), + pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), + config.instrument_coverage, + instr_profile_output_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), + config.instrument_gcov, + pgo_sample_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), + config.debug_info_for_profiling, + llvm_selfprofiler, + selfprofile_before_pass_callback, + selfprofile_after_pass_callback, + extra_passes.as_ptr().cast(), + extra_passes.len(), + llvm_plugins.as_ptr().cast(), + llvm_plugins.len(), + ) + }; result.into_result().map_err(|()| llvm_err(dcx, LlvmError::RunLlvmPasses)) } @@ -617,7 +620,7 @@ pub(crate) unsafe fn optimize( if config.emit_no_opt_bc { let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name); let out = path_to_c_string(&out); - llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); + unsafe { llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()) }; } if let Some(opt_level) = config.opt_level { @@ -627,7 +630,7 @@ pub(crate) unsafe fn optimize( _ if cgcx.opts.cg.linker_plugin_lto.enabled() => llvm::OptStage::PreLinkThinLTO, _ => llvm::OptStage::PreLinkNoLTO, }; - return llvm_optimize(cgcx, dcx, module, config, opt_level, opt_stage); + return unsafe { llvm_optimize(cgcx, dcx, module, config, opt_level, opt_stage) }; } Ok(()) } @@ -692,10 +695,12 @@ pub(crate) unsafe fn codegen( where F: FnOnce(&'ll mut PassManager<'ll>) -> R, { - let cpm = llvm::LLVMCreatePassManager(); - llvm::LLVMAddAnalysisPasses(tm, cpm); - llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); - f(cpm) + unsafe { + let cpm = llvm::LLVMCreatePassManager(); + llvm::LLVMAddAnalysisPasses(tm, cpm); + llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); + f(cpm) + } } // Two things to note: @@ -757,7 +762,9 @@ pub(crate) unsafe fn codegen( let _timer = cgcx .prof .generic_activity_with_arg("LLVM_module_codegen_embed_bitcode", &*module.name); - embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, data); + unsafe { + embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, data); + } } } @@ -793,7 +800,8 @@ pub(crate) unsafe fn codegen( cursor.position() as size_t } - let result = llvm::LLVMRustPrintModule(llmod, out_c.as_ptr(), demangle_callback); + let result = + unsafe { llvm::LLVMRustPrintModule(llmod, out_c.as_ptr(), demangle_callback) }; if result == llvm::LLVMRustResult::Success { record_artifact_size(&cgcx.prof, "llvm_ir", &out); @@ -812,22 +820,24 @@ pub(crate) unsafe fn codegen( // binaries. So we must clone the module to produce the asm output // if we are also producing object code. let llmod = if let EmitObj::ObjectCode(_) = config.emit_obj { - llvm::LLVMCloneModule(llmod) + unsafe { llvm::LLVMCloneModule(llmod) } } else { llmod }; - with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file( - dcx, - tm, - cpm, - llmod, - &path, - None, - llvm::FileType::AssemblyFile, - &cgcx.prof, - ) - })?; + unsafe { + with_codegen(tm, llmod, config.no_builtins, |cpm| { + write_output_file( + dcx, + tm, + cpm, + llmod, + &path, + None, + llvm::FileType::AssemblyFile, + &cgcx.prof, + ) + })?; + } } match config.emit_obj { @@ -851,18 +861,20 @@ pub(crate) unsafe fn codegen( (_, SplitDwarfKind::Split) => Some(dwo_out.as_path()), }; - with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file( - dcx, - tm, - cpm, - llmod, - &obj_out, - dwo_out, - llvm::FileType::ObjectFile, - &cgcx.prof, - ) - })?; + unsafe { + with_codegen(tm, llmod, config.no_builtins, |cpm| { + write_output_file( + dcx, + tm, + cpm, + llmod, + &obj_out, + dwo_out, + llvm::FileType::ObjectFile, + &cgcx.prof, + ) + })?; + } } EmitObj::Bitcode => { @@ -1013,44 +1025,46 @@ unsafe fn embed_bitcode( // reason (see issue #90326 for historical background). let is_aix = target_is_aix(cgcx); let is_apple = target_is_apple(cgcx); - if is_apple || is_aix || cgcx.opts.target_triple.triple().starts_with("wasm") { - // We don't need custom section flags, create LLVM globals. - let llconst = common::bytes_in_context(llcx, bitcode); - let llglobal = llvm::LLVMAddGlobal( - llmod, - common::val_ty(llconst), - c"rustc.embedded.module".as_ptr().cast(), - ); - llvm::LLVMSetInitializer(llglobal, llconst); - - let section = bitcode_section_name(cgcx); - llvm::LLVMSetSection(llglobal, section.as_ptr().cast()); - llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); - llvm::LLVMSetGlobalConstant(llglobal, llvm::True); - - let llconst = common::bytes_in_context(llcx, cmdline.as_bytes()); - let llglobal = llvm::LLVMAddGlobal( - llmod, - common::val_ty(llconst), - c"rustc.embedded.cmdline".as_ptr().cast(), - ); - llvm::LLVMSetInitializer(llglobal, llconst); - let section = if is_apple { - c"__LLVM,__cmdline" - } else if is_aix { - c".info" + unsafe { + if is_apple || is_aix || cgcx.opts.target_triple.triple().starts_with("wasm") { + // We don't need custom section flags, create LLVM globals. + let llconst = common::bytes_in_context(llcx, bitcode); + let llglobal = llvm::LLVMAddGlobal( + llmod, + common::val_ty(llconst), + c"rustc.embedded.module".as_ptr().cast(), + ); + llvm::LLVMSetInitializer(llglobal, llconst); + + let section = bitcode_section_name(cgcx); + llvm::LLVMSetSection(llglobal, section.as_ptr().cast()); + llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); + llvm::LLVMSetGlobalConstant(llglobal, llvm::True); + + let llconst = common::bytes_in_context(llcx, cmdline.as_bytes()); + let llglobal = llvm::LLVMAddGlobal( + llmod, + common::val_ty(llconst), + c"rustc.embedded.cmdline".as_ptr().cast(), + ); + llvm::LLVMSetInitializer(llglobal, llconst); + let section = if is_apple { + c"__LLVM,__cmdline" + } else if is_aix { + c".info" + } else { + c".llvmcmd" + }; + llvm::LLVMSetSection(llglobal, section.as_ptr().cast()); + llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); } else { - c".llvmcmd" - }; - llvm::LLVMSetSection(llglobal, section.as_ptr().cast()); - llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); - } else { - // We need custom section flags, so emit module-level inline assembly. - let section_flags = if cgcx.is_pe_coff { "n" } else { "e" }; - let asm = create_section_with_flags_asm(".llvmbc", section_flags, bitcode); - llvm::LLVMAppendModuleInlineAsm(llmod, asm.as_ptr().cast(), asm.len()); - let asm = create_section_with_flags_asm(".llvmcmd", section_flags, cmdline.as_bytes()); - llvm::LLVMAppendModuleInlineAsm(llmod, asm.as_ptr().cast(), asm.len()); + // We need custom section flags, so emit module-level inline assembly. + let section_flags = if cgcx.is_pe_coff { "n" } else { "e" }; + let asm = create_section_with_flags_asm(".llvmbc", section_flags, bitcode); + llvm::LLVMAppendModuleInlineAsm(llmod, asm.as_ptr().cast(), asm.len()); + let asm = create_section_with_flags_asm(".llvmcmd", section_flags, cmdline.as_bytes()); + llvm::LLVMAppendModuleInlineAsm(llmod, asm.as_ptr().cast(), asm.len()); + } } } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index a2314f4850c7c..164d1681a3670 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -495,8 +495,14 @@ impl<'ll> CodegenCx<'ll, '_> { } // Wasm statics with custom link sections get special treatment as they - // go into custom sections of the wasm executable. - if self.tcx.sess.target.is_like_wasm { + // go into custom sections of the wasm executable. The exception to this + // is the `.init_array` section which are treated specially by the wasm linker. + if self.tcx.sess.target.is_like_wasm + && attrs + .link_section + .map(|link_section| !link_section.as_str().starts_with(".init_array")) + .unwrap_or(true) + { if let Some(section) = attrs.link_section { let section = llvm::LLVMMDStringInContext2( self.llcx, diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 77beb9a6bb386..49677dcf12f73 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -120,7 +120,7 @@ pub unsafe fn create_module<'ll>( ) -> &'ll llvm::Module { let sess = tcx.sess; let mod_name = SmallCStr::new(mod_name); - let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx); + let llmod = unsafe { llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx) }; let mut target_data_layout = sess.target.data_layout.to_string(); let llvm_version = llvm_util::get_version(); @@ -153,11 +153,14 @@ pub unsafe fn create_module<'ll>( // Ensure the data-layout values hardcoded remain the defaults. { let tm = crate::back::write::create_informational_target_machine(tcx.sess); - llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, &tm); + unsafe { + llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, &tm); + } - let llvm_data_layout = llvm::LLVMGetDataLayoutStr(llmod); - let llvm_data_layout = str::from_utf8(CStr::from_ptr(llvm_data_layout).to_bytes()) - .expect("got a non-UTF8 data-layout from LLVM"); + let llvm_data_layout = unsafe { llvm::LLVMGetDataLayoutStr(llmod) }; + let llvm_data_layout = + str::from_utf8(unsafe { CStr::from_ptr(llvm_data_layout) }.to_bytes()) + .expect("got a non-UTF8 data-layout from LLVM"); if target_data_layout != llvm_data_layout { tcx.dcx().emit_err(crate::errors::MismatchedDataLayout { @@ -170,20 +173,28 @@ pub unsafe fn create_module<'ll>( } let data_layout = SmallCStr::new(&target_data_layout); - llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr()); + unsafe { + llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr()); + } let llvm_target = SmallCStr::new(&sess.target.llvm_target); - llvm::LLVMRustSetNormalizedTarget(llmod, llvm_target.as_ptr()); + unsafe { + llvm::LLVMRustSetNormalizedTarget(llmod, llvm_target.as_ptr()); + } let reloc_model = sess.relocation_model(); if matches!(reloc_model, RelocModel::Pic | RelocModel::Pie) { - llvm::LLVMRustSetModulePICLevel(llmod); + unsafe { + llvm::LLVMRustSetModulePICLevel(llmod); + } // PIE is potentially more effective than PIC, but can only be used in executables. // If all our outputs are executables, then we can relax PIC to PIE. if reloc_model == RelocModel::Pie || tcx.crate_types().iter().all(|ty| *ty == CrateType::Executable) { - llvm::LLVMRustSetModulePIELevel(llmod); + unsafe { + llvm::LLVMRustSetModulePIELevel(llmod); + } } } @@ -192,95 +203,109 @@ pub unsafe fn create_module<'ll>( // longer jumps) if a larger code model is used with a smaller one. // // See https://reviews.llvm.org/D52322 and https://reviews.llvm.org/D52323. - llvm::LLVMRustSetModuleCodeModel(llmod, to_llvm_code_model(sess.code_model())); + unsafe { + llvm::LLVMRustSetModuleCodeModel(llmod, to_llvm_code_model(sess.code_model())); + } // If skipping the PLT is enabled, we need to add some module metadata // to ensure intrinsic calls don't use it. if !sess.needs_plt() { let avoid_plt = c"RtLibUseGOT".as_ptr().cast(); - llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1); + unsafe { + llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1); + } } // Enable canonical jump tables if CFI is enabled. (See https://reviews.llvm.org/D65629.) if sess.is_sanitizer_cfi_canonical_jump_tables_enabled() && sess.is_sanitizer_cfi_enabled() { let canonical_jump_tables = c"CFI Canonical Jump Tables".as_ptr().cast(); - llvm::LLVMRustAddModuleFlagU32( - llmod, - llvm::LLVMModFlagBehavior::Override, - canonical_jump_tables, - 1, - ); + unsafe { + llvm::LLVMRustAddModuleFlagU32( + llmod, + llvm::LLVMModFlagBehavior::Override, + canonical_jump_tables, + 1, + ); + } } // Enable LTO unit splitting if specified or if CFI is enabled. (See https://reviews.llvm.org/D53891.) if sess.is_split_lto_unit_enabled() || sess.is_sanitizer_cfi_enabled() { let enable_split_lto_unit = c"EnableSplitLTOUnit".as_ptr().cast(); - llvm::LLVMRustAddModuleFlagU32( - llmod, - llvm::LLVMModFlagBehavior::Override, - enable_split_lto_unit, - 1, - ); + unsafe { + llvm::LLVMRustAddModuleFlagU32( + llmod, + llvm::LLVMModFlagBehavior::Override, + enable_split_lto_unit, + 1, + ); + } } // Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.) if sess.is_sanitizer_kcfi_enabled() { let kcfi = c"kcfi".as_ptr().cast(); - llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1); + unsafe { + llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1); + } } // Control Flow Guard is currently only supported by the MSVC linker on Windows. if sess.target.is_like_msvc { - match sess.opts.cg.control_flow_guard { - CFGuard::Disabled => {} - CFGuard::NoChecks => { - // Set `cfguard=1` module flag to emit metadata only. - llvm::LLVMRustAddModuleFlagU32( - llmod, - llvm::LLVMModFlagBehavior::Warning, - c"cfguard".as_ptr() as *const _, - 1, - ) - } - CFGuard::Checks => { - // Set `cfguard=2` module flag to emit metadata and checks. - llvm::LLVMRustAddModuleFlagU32( - llmod, - llvm::LLVMModFlagBehavior::Warning, - c"cfguard".as_ptr() as *const _, - 2, - ) + unsafe { + match sess.opts.cg.control_flow_guard { + CFGuard::Disabled => {} + CFGuard::NoChecks => { + // Set `cfguard=1` module flag to emit metadata only. + llvm::LLVMRustAddModuleFlagU32( + llmod, + llvm::LLVMModFlagBehavior::Warning, + c"cfguard".as_ptr() as *const _, + 1, + ) + } + CFGuard::Checks => { + // Set `cfguard=2` module flag to emit metadata and checks. + llvm::LLVMRustAddModuleFlagU32( + llmod, + llvm::LLVMModFlagBehavior::Warning, + c"cfguard".as_ptr() as *const _, + 2, + ) + } } } } if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection { if sess.target.arch == "aarch64" { - llvm::LLVMRustAddModuleFlagU32( - llmod, - llvm::LLVMModFlagBehavior::Min, - c"branch-target-enforcement".as_ptr().cast(), - bti.into(), - ); - llvm::LLVMRustAddModuleFlagU32( - llmod, - llvm::LLVMModFlagBehavior::Min, - c"sign-return-address".as_ptr().cast(), - pac_ret.is_some().into(), - ); - let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A }); - llvm::LLVMRustAddModuleFlagU32( - llmod, - llvm::LLVMModFlagBehavior::Min, - c"sign-return-address-all".as_ptr().cast(), - pac_opts.leaf.into(), - ); - llvm::LLVMRustAddModuleFlagU32( - llmod, - llvm::LLVMModFlagBehavior::Min, - c"sign-return-address-with-bkey".as_ptr().cast(), - u32::from(pac_opts.key == PAuthKey::B), - ); + unsafe { + llvm::LLVMRustAddModuleFlagU32( + llmod, + llvm::LLVMModFlagBehavior::Min, + c"branch-target-enforcement".as_ptr().cast(), + bti.into(), + ); + llvm::LLVMRustAddModuleFlagU32( + llmod, + llvm::LLVMModFlagBehavior::Min, + c"sign-return-address".as_ptr().cast(), + pac_ret.is_some().into(), + ); + let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A }); + llvm::LLVMRustAddModuleFlagU32( + llmod, + llvm::LLVMModFlagBehavior::Min, + c"sign-return-address-all".as_ptr().cast(), + pac_opts.leaf.into(), + ); + llvm::LLVMRustAddModuleFlagU32( + llmod, + llvm::LLVMModFlagBehavior::Min, + c"sign-return-address-with-bkey".as_ptr().cast(), + u32::from(pac_opts.key == PAuthKey::B), + ); + } } else { bug!( "branch-protection used on non-AArch64 target; \ @@ -291,39 +316,47 @@ pub unsafe fn create_module<'ll>( // Pass on the control-flow protection flags to LLVM (equivalent to `-fcf-protection` in Clang). if let CFProtection::Branch | CFProtection::Full = sess.opts.unstable_opts.cf_protection { - llvm::LLVMRustAddModuleFlagU32( - llmod, - llvm::LLVMModFlagBehavior::Override, - c"cf-protection-branch".as_ptr().cast(), - 1, - ) + unsafe { + llvm::LLVMRustAddModuleFlagU32( + llmod, + llvm::LLVMModFlagBehavior::Override, + c"cf-protection-branch".as_ptr().cast(), + 1, + ); + } } if let CFProtection::Return | CFProtection::Full = sess.opts.unstable_opts.cf_protection { - llvm::LLVMRustAddModuleFlagU32( - llmod, - llvm::LLVMModFlagBehavior::Override, - c"cf-protection-return".as_ptr().cast(), - 1, - ) + unsafe { + llvm::LLVMRustAddModuleFlagU32( + llmod, + llvm::LLVMModFlagBehavior::Override, + c"cf-protection-return".as_ptr().cast(), + 1, + ); + } } if sess.opts.unstable_opts.virtual_function_elimination { - llvm::LLVMRustAddModuleFlagU32( - llmod, - llvm::LLVMModFlagBehavior::Error, - c"Virtual Function Elim".as_ptr().cast(), - 1, - ); + unsafe { + llvm::LLVMRustAddModuleFlagU32( + llmod, + llvm::LLVMModFlagBehavior::Error, + c"Virtual Function Elim".as_ptr().cast(), + 1, + ); + } } // Set module flag to enable Windows EHCont Guard (/guard:ehcont). if sess.opts.unstable_opts.ehcont_guard { - llvm::LLVMRustAddModuleFlagU32( - llmod, - llvm::LLVMModFlagBehavior::Warning, - c"ehcontguard".as_ptr() as *const _, - 1, - ) + unsafe { + llvm::LLVMRustAddModuleFlagU32( + llmod, + llvm::LLVMModFlagBehavior::Warning, + c"ehcontguard".as_ptr() as *const _, + 1, + ) + } } // Insert `llvm.ident` metadata. @@ -333,16 +366,20 @@ pub unsafe fn create_module<'ll>( #[allow(clippy::option_env_unwrap)] let rustc_producer = format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION")); - let name_metadata = llvm::LLVMMDStringInContext( - llcx, - rustc_producer.as_ptr().cast(), - rustc_producer.as_bytes().len() as c_uint, - ); - llvm::LLVMAddNamedMetadataOperand( - llmod, - c"llvm.ident".as_ptr(), - llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1), - ); + let name_metadata = unsafe { + llvm::LLVMMDStringInContext( + llcx, + rustc_producer.as_ptr().cast(), + rustc_producer.as_bytes().len() as c_uint, + ) + }; + unsafe { + llvm::LLVMAddNamedMetadataOperand( + llmod, + c"llvm.ident".as_ptr(), + llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1), + ); + } // Emit RISC-V specific target-abi metadata // to workaround lld as the LTO plugin not @@ -351,13 +388,15 @@ pub unsafe fn create_module<'ll>( // If llvm_abiname is empty, emit nothing. let llvm_abiname = &sess.target.options.llvm_abiname; if matches!(sess.target.arch.as_ref(), "riscv32" | "riscv64") && !llvm_abiname.is_empty() { - llvm::LLVMRustAddModuleFlagString( - llmod, - llvm::LLVMModFlagBehavior::Error, - c"target-abi".as_ptr(), - llvm_abiname.as_ptr().cast(), - llvm_abiname.len(), - ); + unsafe { + llvm::LLVMRustAddModuleFlagString( + llmod, + llvm::LLVMModFlagBehavior::Error, + c"target-abi".as_ptr(), + llvm_abiname.as_ptr().cast(), + llvm_abiname.len(), + ); + } } // Add module flags specified via -Z llvm_module_flag @@ -375,7 +414,7 @@ pub unsafe fn create_module<'ll>( // We already checked this during option parsing _ => unreachable!(), }; - llvm::LLVMRustAddModuleFlagU32(llmod, behavior, key.as_ptr().cast(), *value) + unsafe { llvm::LLVMRustAddModuleFlagU32(llmod, behavior, key.as_ptr().cast(), *value) } } llmod diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index ed0989a0ba413..a96993b9aba74 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -216,7 +216,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { module: &ModuleCodegen, config: &ModuleConfig, ) -> Result<(), FatalError> { - back::write::optimize(cgcx, dcx, module, config) + unsafe { back::write::optimize(cgcx, dcx, module, config) } } fn optimize_fat( cgcx: &CodegenContext, @@ -230,7 +230,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { cgcx: &CodegenContext, thin: ThinModule, ) -> Result, FatalError> { - back::lto::optimize_thin_module(thin, cgcx) + unsafe { back::lto::optimize_thin_module(thin, cgcx) } } unsafe fn codegen( cgcx: &CodegenContext, @@ -238,7 +238,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { module: ModuleCodegen, config: &ModuleConfig, ) -> Result { - back::write::codegen(cgcx, dcx, module, config) + unsafe { back::write::codegen(cgcx, dcx, module, config) } } fn prepare_thin( module: ModuleCodegen, diff --git a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs index f9b28178ddb97..73e1b08a3d77a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs @@ -40,7 +40,7 @@ impl<'ll> OptimizationDiagnostic<'ll> { let mut filename = None; let pass_name = super::build_string(|pass_name| { message = super::build_string(|message| { - filename = super::build_string(|filename| { + filename = super::build_string(|filename| unsafe { super::LLVMRustUnpackOptimizationDiagnostic( di, pass_name, @@ -91,7 +91,7 @@ impl SrcMgrDiagnostic { let mut ranges = [0; 8]; let mut num_ranges = ranges.len() / 2; let message = super::build_string(|message| { - buffer = super::build_string(|buffer| { + buffer = super::build_string(|buffer| unsafe { have_source = super::LLVMRustUnpackSMDiagnostic( diag, message, @@ -134,7 +134,9 @@ impl InlineAsmDiagnostic { let mut message = None; let mut level = super::DiagnosticLevel::Error; - super::LLVMRustUnpackInlineAsmDiagnostic(di, &mut level, &mut cookie, &mut message); + unsafe { + super::LLVMRustUnpackInlineAsmDiagnostic(di, &mut level, &mut cookie, &mut message); + } InlineAsmDiagnostic { level, @@ -146,7 +148,8 @@ impl InlineAsmDiagnostic { unsafe fn unpackSrcMgr(di: &DiagnosticInfo) -> Self { let mut cookie = 0; - let smdiag = SrcMgrDiagnostic::unpack(super::LLVMRustGetSMDiagnostic(di, &mut cookie)); + let smdiag = + unsafe { SrcMgrDiagnostic::unpack(super::LLVMRustGetSMDiagnostic(di, &mut cookie)) }; InlineAsmDiagnostic { level: smdiag.level, cookie: cookie.into(), @@ -170,44 +173,46 @@ pub enum Diagnostic<'ll> { impl<'ll> Diagnostic<'ll> { pub unsafe fn unpack(di: &'ll DiagnosticInfo) -> Self { use super::DiagnosticKind as Dk; - let kind = super::LLVMRustGetDiagInfoKind(di); - match kind { - Dk::InlineAsm => InlineAsm(InlineAsmDiagnostic::unpackInlineAsm(di)), + unsafe { + let kind = super::LLVMRustGetDiagInfoKind(di); + match kind { + Dk::InlineAsm => InlineAsm(InlineAsmDiagnostic::unpackInlineAsm(di)), - Dk::OptimizationRemark => { - Optimization(OptimizationDiagnostic::unpack(OptimizationRemark, di)) - } - Dk::OptimizationRemarkOther => { - Optimization(OptimizationDiagnostic::unpack(OptimizationRemarkOther, di)) - } - Dk::OptimizationRemarkMissed => { - Optimization(OptimizationDiagnostic::unpack(OptimizationMissed, di)) - } + Dk::OptimizationRemark => { + Optimization(OptimizationDiagnostic::unpack(OptimizationRemark, di)) + } + Dk::OptimizationRemarkOther => { + Optimization(OptimizationDiagnostic::unpack(OptimizationRemarkOther, di)) + } + Dk::OptimizationRemarkMissed => { + Optimization(OptimizationDiagnostic::unpack(OptimizationMissed, di)) + } - Dk::OptimizationRemarkAnalysis => { - Optimization(OptimizationDiagnostic::unpack(OptimizationAnalysis, di)) - } + Dk::OptimizationRemarkAnalysis => { + Optimization(OptimizationDiagnostic::unpack(OptimizationAnalysis, di)) + } - Dk::OptimizationRemarkAnalysisFPCommute => { - Optimization(OptimizationDiagnostic::unpack(OptimizationAnalysisFPCommute, di)) - } + Dk::OptimizationRemarkAnalysisFPCommute => { + Optimization(OptimizationDiagnostic::unpack(OptimizationAnalysisFPCommute, di)) + } - Dk::OptimizationRemarkAnalysisAliasing => { - Optimization(OptimizationDiagnostic::unpack(OptimizationAnalysisAliasing, di)) - } + Dk::OptimizationRemarkAnalysisAliasing => { + Optimization(OptimizationDiagnostic::unpack(OptimizationAnalysisAliasing, di)) + } - Dk::OptimizationFailure => { - Optimization(OptimizationDiagnostic::unpack(OptimizationFailure, di)) - } + Dk::OptimizationFailure => { + Optimization(OptimizationDiagnostic::unpack(OptimizationFailure, di)) + } - Dk::PGOProfile => PGO(di), - Dk::Linker => Linker(di), - Dk::Unsupported => Unsupported(di), + Dk::PGOProfile => PGO(di), + Dk::Linker => Linker(di), + Dk::Unsupported => Unsupported(di), - Dk::SrcMgr => InlineAsm(InlineAsmDiagnostic::unpackSrcMgr(di)), + Dk::SrcMgr => InlineAsm(InlineAsmDiagnostic::unpackSrcMgr(di)), - _ => UnknownDiagnostic(di), + _ => UnknownDiagnostic(di), + } } } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e0bf6110cdf00..ae46200d3f554 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2440,4 +2440,8 @@ extern "C" { callback: GetSymbolsCallback, error_callback: GetSymbolsErrorCallback, ) -> *mut c_void; + + pub fn LLVMRustIs64BitSymbolicFile(buf_ptr: *const u8, buf_len: usize) -> bool; + + pub fn LLVMRustIsECObject(buf_ptr: *const u8, buf_len: usize) -> bool; } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 0e89e66be49a0..98dc8ac86d2b3 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -49,12 +49,16 @@ unsafe fn configure_llvm(sess: &Session) { let mut llvm_c_strs = Vec::with_capacity(n_args + 1); let mut llvm_args = Vec::with_capacity(n_args + 1); - llvm::LLVMRustInstallErrorHandlers(); + unsafe { + llvm::LLVMRustInstallErrorHandlers(); + } // On Windows, an LLVM assertion will open an Abort/Retry/Ignore dialog // box for the purpose of launching a debugger. However, on CI this will // cause it to hang until it times out, which can take several hours. if std::env::var_os("CI").is_some() { - llvm::LLVMRustDisableSystemDialogsOnCrash(); + unsafe { + llvm::LLVMRustDisableSystemDialogsOnCrash(); + } } fn llvm_arg_to_arg_name(full_arg: &str) -> &str { @@ -124,12 +128,12 @@ unsafe fn configure_llvm(sess: &Session) { } if sess.opts.unstable_opts.llvm_time_trace { - llvm::LLVMRustTimeTraceProfilerInitialize(); + unsafe { llvm::LLVMRustTimeTraceProfilerInitialize() }; } rustc_llvm::initialize_available_targets(); - llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, llvm_args.as_ptr()); + unsafe { llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, llvm_args.as_ptr()) }; } pub fn time_trace_profiler_finish(file_name: &Path) { @@ -442,8 +446,8 @@ pub(crate) fn print(req: &PrintRequest, mut out: &mut String, sess: &Session) { let cpu_cstring = CString::new(handle_native(sess.target.cpu.as_ref())) .unwrap_or_else(|e| bug!("failed to convert to cstring: {}", e)); unsafe extern "C" fn callback(out: *mut c_void, string: *const c_char, len: usize) { - let out = &mut *(out as *mut &mut String); - let bytes = slice::from_raw_parts(string as *const u8, len); + let out = unsafe { &mut *(out as *mut &mut String) }; + let bytes = unsafe { slice::from_raw_parts(string as *const u8, len) }; write!(out, "{}", String::from_utf8_lossy(bytes)).unwrap(); } unsafe { diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index a7df08421a31e..282a186be99ac 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -108,8 +108,8 @@ impl CodegenCx<'_, '_> { llval: &llvm::Value, is_declaration: bool, ) -> bool { - let linkage = llvm::LLVMRustGetLinkage(llval); - let visibility = llvm::LLVMRustGetVisibility(llval); + let linkage = unsafe { llvm::LLVMRustGetLinkage(llval) }; + let visibility = unsafe { llvm::LLVMRustGetVisibility(llval) }; if matches!(linkage, llvm::Linkage::InternalLinkage | llvm::Linkage::PrivateLinkage) { return true; @@ -145,8 +145,8 @@ impl CodegenCx<'_, '_> { } // Thread-local variables generally don't support copy relocations. - let is_thread_local_var = llvm::LLVMIsAGlobalVariable(llval) - .is_some_and(|v| llvm::LLVMIsThreadLocal(v) == llvm::True); + let is_thread_local_var = unsafe { llvm::LLVMIsAGlobalVariable(llval) } + .is_some_and(|v| unsafe { llvm::LLVMIsThreadLocal(v) } == llvm::True); if is_thread_local_var { return false; } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 3771fc6b0a271..f7b5b0f310b67 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -ar_archive_writer = "0.2.0" +ar_archive_writer = "0.3.0" arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" cc = "1.0.90" diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index c99118f5156d1..ae649cd77c420 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -6,8 +6,8 @@ use rustc_span::symbol::Symbol; use super::metadata::search_for_section; -pub use ar_archive_writer::get_native_object_symbols; use ar_archive_writer::{write_archive_to_stream, ArchiveKind, NewArchiveMember}; +pub use ar_archive_writer::{ObjectReader, DEFAULT_OBJECT_READER}; use object::read::archive::ArchiveFile; use object::read::macho::FatArch; use tempfile::Builder as TempFileBuilder; @@ -89,8 +89,7 @@ pub trait ArchiveBuilder { #[must_use = "must call build() to finish building the archive"] pub struct ArArchiveBuilder<'a> { sess: &'a Session, - get_object_symbols: - fn(buf: &[u8], f: &mut dyn FnMut(&[u8]) -> io::Result<()>) -> io::Result, + object_reader: &'static ObjectReader, src_archives: Vec<(PathBuf, Mmap)>, // Don't use an `HashMap` here, as the order is important. `lib.rmeta` needs @@ -105,14 +104,8 @@ enum ArchiveEntry { } impl<'a> ArArchiveBuilder<'a> { - pub fn new( - sess: &'a Session, - get_object_symbols: fn( - buf: &[u8], - f: &mut dyn FnMut(&[u8]) -> io::Result<()>, - ) -> io::Result, - ) -> ArArchiveBuilder<'a> { - ArArchiveBuilder { sess, get_object_symbols, src_archives: vec![], entries: vec![] } + pub fn new(sess: &'a Session, object_reader: &'static ObjectReader) -> ArArchiveBuilder<'a> { + ArArchiveBuilder { sess, object_reader, src_archives: vec![], entries: vec![] } } } @@ -267,7 +260,7 @@ impl<'a> ArArchiveBuilder<'a> { entries.push(NewArchiveMember { buf: data, - get_symbols: self.get_object_symbols, + object_reader: self.object_reader, member_name: String::from_utf8(entry_name).unwrap(), mtime: 0, uid: 0, @@ -294,7 +287,13 @@ impl<'a> ArArchiveBuilder<'a> { let mut archive_tmpfile = File::create_new(&archive_tmpfile_path) .map_err(|err| io_error_context("couldn't create the temp file", err))?; - write_archive_to_stream(&mut archive_tmpfile, &entries, archive_kind, false)?; + write_archive_to_stream( + &mut archive_tmpfile, + &entries, + archive_kind, + false, + /* is_ec = */ self.sess.target.arch == "arm64ec", + )?; let any_entries = !entries.is_empty(); drop(entries); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 1f627353d54e1..8e07d128dbd64 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -750,7 +750,7 @@ fn link_natively( for print in &sess.opts.prints { if print.kind == PrintKind::LinkArgs { - let content = format!("{cmd:?}"); + let content = format!("{cmd:?}\n"); print.out.overwrite(&content, sess); } } diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index cb6244050df24..5291cad148e26 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -72,7 +72,7 @@ impl LtoModuleCodegen { B::optimize_fat(cgcx, &mut module)?; Ok(module) } - LtoModuleCodegen::Thin(thin) => B::optimize_thin(cgcx, thin), + LtoModuleCodegen::Thin(thin) => unsafe { B::optimize_thin(cgcx, thin) }, } } diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 822f5c2c44a2e..35e9a3b7dc206 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -37,13 +37,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn eval_unevaluated_mir_constant_to_valtree( &self, constant: &mir::ConstOperand<'tcx>, - ) -> Result>, ErrorHandled> { + ) -> Result, Ty<'tcx>>, ErrorHandled> { let uv = match self.monomorphize(constant.const_) { mir::Const::Unevaluated(uv, _) => uv.shrink(), mir::Const::Ty(_, c) => match c.kind() { // A constant that came from a const generic but was then used as an argument to old-style // simd_shuffle (passing as argument instead of as a generic param). - rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Some(valtree)), + rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Ok(valtree)), other => span_bug!(constant.span, "{other:#?}"), }, // We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate @@ -70,6 +70,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = self .eval_unevaluated_mir_constant_to_valtree(constant) .ok() + .map(|x| x.ok()) .flatten() .map(|val| { let field_ty = ty.builtin_index().unwrap(); diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 4ae4816e33ab9..3a6dc81eff11f 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -27,15 +27,15 @@ pub(crate) use valtrees::{eval_to_valtree, valtree_to_const_value}; // We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes. const VALTREE_MAX_NODES: usize = 100000; -pub(crate) enum ValTreeCreationError { +pub(crate) enum ValTreeCreationError<'tcx> { NodesOverflow, /// Values of this type, or this particular value, are not supported as valtrees. - NonSupportedType, + NonSupportedType(Ty<'tcx>), } -pub(crate) type ValTreeCreationResult<'tcx> = Result, ValTreeCreationError>; +pub(crate) type ValTreeCreationResult<'tcx> = Result, ValTreeCreationError<'tcx>>; -impl From> for ValTreeCreationError { - fn from(err: InterpErrorInfo<'_>) -> Self { +impl<'tcx> From> for ValTreeCreationError<'tcx> { + fn from(err: InterpErrorInfo<'tcx>) -> Self { ty::tls::with(|tcx| { bug!( "Unexpected Undefined Behavior error during valtree construction: {}", diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 2e8ad445cf5e8..3bc01510730b4 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -120,13 +120,13 @@ fn const_to_valtree_inner<'tcx>( // We could allow wide raw pointers where both sides are integers in the future, // but for now we reject them. if matches!(val.layout.abi, Abi::ScalarPair(..)) { - return Err(ValTreeCreationError::NonSupportedType); + return Err(ValTreeCreationError::NonSupportedType(ty)); } let val = val.to_scalar(); // We are in the CTFE machine, so ptr-to-int casts will fail. // This can only be `Ok` if `val` already is an integer. let Ok(val) = val.try_to_scalar_int() else { - return Err(ValTreeCreationError::NonSupportedType); + return Err(ValTreeCreationError::NonSupportedType(ty)); }; // It's just a ScalarInt! Ok(ty::ValTree::Leaf(val)) @@ -134,7 +134,7 @@ fn const_to_valtree_inner<'tcx>( // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to // agree with runtime equality tests. - ty::FnPtr(_) => Err(ValTreeCreationError::NonSupportedType), + ty::FnPtr(_) => Err(ValTreeCreationError::NonSupportedType(ty)), ty::Ref(_, _, _) => { let derefd_place = ecx.deref_pointer(place)?; @@ -148,7 +148,7 @@ fn const_to_valtree_inner<'tcx>( // resolving their backing type, even if we can do that at const eval time. We may // hypothetically be able to allow `dyn StructuralPartialEq` trait objects in the future, // but it is unclear if this is useful. - ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType), + ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType(ty)), ty::Tuple(elem_tys) => { branches(ecx, place, elem_tys.len(), None, num_nodes) @@ -156,7 +156,7 @@ fn const_to_valtree_inner<'tcx>( ty::Adt(def, _) => { if def.is_union() { - return Err(ValTreeCreationError::NonSupportedType); + return Err(ValTreeCreationError::NonSupportedType(ty)); } else if def.variants().is_empty() { bug!("uninhabited types should have errored and never gotten converted to valtree") } @@ -180,7 +180,7 @@ fn const_to_valtree_inner<'tcx>( | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) - | ty::CoroutineWitness(..) => Err(ValTreeCreationError::NonSupportedType), + | ty::CoroutineWitness(..) => Err(ValTreeCreationError::NonSupportedType(ty)), } } @@ -251,7 +251,7 @@ pub(crate) fn eval_to_valtree<'tcx>( let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes); match valtree_result { - Ok(valtree) => Ok(Some(valtree)), + Ok(valtree) => Ok(Ok(valtree)), Err(err) => { let did = cid.instance.def_id(); let global_const_id = cid.display(tcx); @@ -262,7 +262,7 @@ pub(crate) fn eval_to_valtree<'tcx>( tcx.dcx().emit_err(MaxNumNodesInConstErr { span, global_const_id }); Err(handled.into()) } - ValTreeCreationError::NonSupportedType => Ok(None), + ValTreeCreationError::NonSupportedType(ty) => Ok(Err(ty)), } } } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 83b61ab17492c..bd2a5812cfad2 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -401,15 +401,46 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } (ty::Dynamic(data_a, _, ty::Dyn), ty::Dynamic(data_b, _, ty::Dyn)) => { let val = self.read_immediate(src)?; - if data_a.principal() == data_b.principal() { - // A NOP cast that doesn't actually change anything, should be allowed even with mismatching vtables. - // (But currently mismatching vtables violate the validity invariant so UB is triggered anyway.) - return self.write_immediate(*val, dest); - } + // Take apart the old pointer, and find the dynamic type. let (old_data, old_vptr) = val.to_scalar_pair(); let old_data = old_data.to_pointer(self)?; let old_vptr = old_vptr.to_pointer(self)?; let ty = self.get_ptr_vtable_ty(old_vptr, Some(data_a))?; + + // Sanity-check that `supertrait_vtable_slot` in this type's vtable indeed produces + // our destination trait. + if cfg!(debug_assertions) { + let vptr_entry_idx = + self.tcx.supertrait_vtable_slot((src_pointee_ty, dest_pointee_ty)); + let vtable_entries = self.vtable_entries(data_a.principal(), ty); + if let Some(entry_idx) = vptr_entry_idx { + let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) = + vtable_entries.get(entry_idx) + else { + span_bug!( + self.cur_span(), + "invalid vtable entry index in {} -> {} upcast", + src_pointee_ty, + dest_pointee_ty + ); + }; + let erased_trait_ref = upcast_trait_ref + .map_bound(|r| ty::ExistentialTraitRef::erase_self_ty(*self.tcx, r)); + assert!( + data_b + .principal() + .is_some_and(|b| self.eq_in_param_env(erased_trait_ref, b)) + ); + } else { + // In this case codegen would keep using the old vtable. We don't want to do + // that as it has the wrong trait. The reason codegen can do this is that + // one vtable is a prefix of the other, so we double-check that. + let vtable_entries_b = self.vtable_entries(data_b.principal(), ty); + assert!(&vtable_entries[..vtable_entries_b.len()] == vtable_entries_b); + }; + } + + // Get the destination trait vtable and return that. let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?; self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 6d3e5ea103148..9fddeec2973a5 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -2,11 +2,15 @@ use std::cell::Cell; use std::{fmt, mem}; use either::{Either, Left, Right}; +use rustc_infer::infer::at::ToTrace; +use rustc_infer::traits::ObligationCause; +use rustc_trait_selection::traits::ObligationCtxt; use tracing::{debug, info, info_span, instrument, trace}; use rustc_errors::DiagCtxtHandle; use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData}; use rustc_index::IndexVec; +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir; use rustc_middle::mir::interpret::{ CtfeProvenance, ErrorHandled, InvalidMetaKind, ReportedErrorInfo, @@ -640,6 +644,32 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + /// Check if the two things are equal in the current param_env, using an infctx to get proper + /// equality checks. + pub(super) fn eq_in_param_env(&self, a: T, b: T) -> bool + where + T: PartialEq + TypeFoldable> + ToTrace<'tcx>, + { + // Fast path: compare directly. + if a == b { + return true; + } + // Slow path: spin up an inference context to check if these traits are sufficiently equal. + let infcx = self.tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + let cause = ObligationCause::dummy_with_span(self.cur_span()); + // equate the two trait refs after normalization + let a = ocx.normalize(&cause, self.param_env, a); + let b = ocx.normalize(&cause, self.param_env, b); + if ocx.eq(&cause, self.param_env, a, b).is_ok() { + if ocx.select_all_or_error().is_empty() { + // All good. + return true; + } + } + return false; + } + /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a /// frame which is not `#[track_caller]`. This matches the `caller_location` intrinsic, /// and is primarily intended for the panic machinery. diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 25f6bd640554e..56d3dc9410412 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -1,7 +1,6 @@ use std::borrow::Cow; use either::Either; -use rustc_middle::ty::TyCtxt; use tracing::trace; use rustc_middle::{ @@ -867,7 +866,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; // Obtain the underlying trait we are working on, and the adjusted receiver argument. - let (dyn_trait, dyn_ty, adjusted_recv) = if let ty::Dynamic(data, _, ty::DynStar) = + let (trait_, dyn_ty, adjusted_recv) = if let ty::Dynamic(data, _, ty::DynStar) = receiver_place.layout.ty.kind() { let recv = self.unpack_dyn_star(&receiver_place, data)?; @@ -898,20 +897,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { (receiver_trait.principal(), dyn_ty, receiver_place.ptr()) }; - // Now determine the actual method to call. We can do that in two different ways and - // compare them to ensure everything fits. - let vtable_entries = if let Some(dyn_trait) = dyn_trait { - let trait_ref = dyn_trait.with_self_ty(*self.tcx, dyn_ty); - let trait_ref = self.tcx.erase_regions(trait_ref); - self.tcx.vtable_entries(trait_ref) - } else { - TyCtxt::COMMON_VTABLE_ENTRIES - }; + // Now determine the actual method to call. Usually we use the easy way of just + // looking up the method at index `idx`. + let vtable_entries = self.vtable_entries(trait_, dyn_ty); let Some(ty::VtblEntry::Method(fn_inst)) = vtable_entries.get(idx).copied() else { // FIXME(fee1-dead) these could be variants of the UB info enum instead of this throw_ub_custom!(fluent::const_eval_dyn_call_not_a_method); }; trace!("Virtual call dispatches to {fn_inst:#?}"); + // We can also do the lookup based on `def_id` and `dyn_ty`, and check that that + // produces the same result. if cfg!(debug_assertions) { let tcx = *self.tcx; diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index bd2c651942185..fb50661b8263d 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -1,10 +1,7 @@ -use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits::ObligationCause; use rustc_middle::mir::interpret::{InterpResult, Pointer}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyCtxt, VtblEntry}; use rustc_target::abi::{Align, Size}; -use rustc_trait_selection::traits::ObligationCtxt; use tracing::trace; use super::util::ensure_monomorphic_enough; @@ -47,6 +44,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok((layout.size, layout.align.abi)) } + pub(super) fn vtable_entries( + &self, + trait_: Option>, + dyn_ty: Ty<'tcx>, + ) -> &'tcx [VtblEntry<'tcx>] { + if let Some(trait_) = trait_ { + let trait_ref = trait_.with_self_ty(*self.tcx, dyn_ty); + let trait_ref = self.tcx.erase_regions(trait_ref); + self.tcx.vtable_entries(trait_ref) + } else { + TyCtxt::COMMON_VTABLE_ENTRIES + } + } + /// Check that the given vtable trait is valid for a pointer/reference/place with the given /// expected trait type. pub(super) fn check_vtable_for_type( @@ -54,28 +65,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { vtable_trait: Option>, expected_trait: &'tcx ty::List>, ) -> InterpResult<'tcx> { - // Fast path: if they are equal, it's all fine. - if expected_trait.principal() == vtable_trait { - return Ok(()); - } - if let (Some(expected_trait), Some(vtable_trait)) = - (expected_trait.principal(), vtable_trait) - { - // Slow path: spin up an inference context to check if these traits are sufficiently equal. - let infcx = self.tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(&infcx); - let cause = ObligationCause::dummy_with_span(self.cur_span()); - // equate the two trait refs after normalization - let expected_trait = ocx.normalize(&cause, self.param_env, expected_trait); - let vtable_trait = ocx.normalize(&cause, self.param_env, vtable_trait); - if ocx.eq(&cause, self.param_env, expected_trait, vtable_trait).is_ok() { - if ocx.select_all_or_error().is_empty() { - // All good. - return Ok(()); - } - } + let eq = match (expected_trait.principal(), vtable_trait) { + (Some(a), Some(b)) => self.eq_in_param_env(a, b), + (None, None) => true, + _ => false, + }; + if !eq { + throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); } - throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); + Ok(()) } /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 7fea061766666..4da7e233889f0 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -34,7 +34,6 @@ use super::{ Pointer, Projectable, Scalar, ValueVisitor, }; -// for the validation errors use super::InterpError::UndefinedBehavior as Ub; use super::InterpError::Unsupported as Unsup; use super::UndefinedBehaviorInfo::*; diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index e5e733439ea05..3794a6e043c6d 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -56,5 +56,5 @@ portable-atomic = "1.5.1" [features] # tidy-alphabetical-start -rustc_use_parallel_compiler = ["indexmap/rustc-rayon", "rustc-rayon"] +rustc_use_parallel_compiler = ["indexmap/rustc-rayon", "dep:rustc-rayon"] # tidy-alphabetical-end diff --git a/compiler/rustc_error_codes/src/error_codes/E0120.md b/compiler/rustc_error_codes/src/error_codes/E0120.md index dc7258d87317f..aa701df577465 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0120.md +++ b/compiler/rustc_error_codes/src/error_codes/E0120.md @@ -1,7 +1,7 @@ -Drop was implemented on a trait, which is not allowed: only structs and -enums can implement Drop. +`Drop` was implemented on a trait object or reference, which is not allowed; +only structs, enums, and unions can implement Drop. -Erroneous code example: +Erroneous code examples: ```compile_fail,E0120 trait MyTrait {} @@ -11,8 +11,16 @@ impl Drop for MyTrait { } ``` -A workaround for this problem is to wrap the trait up in a struct, and implement -Drop on that: +```compile_fail,E0120 +struct Concrete {} + +impl Drop for &'_ mut Concrete { + fn drop(&mut self) {} +} +``` + +A workaround for traits is to create a wrapper struct with a generic type, +add a trait bound to the type, and implement `Drop` on the wrapper: ``` trait MyTrait {} @@ -24,13 +32,13 @@ impl Drop for MyWrapper { ``` -Alternatively, wrapping trait objects requires something: +Alternatively, the `Drop` wrapper can contain the trait object: ``` trait MyTrait {} -//or Box, if you wanted an owned trait object -struct MyWrapper<'a> { foo: &'a MyTrait } +// or Box, if you wanted an owned trait object +struct MyWrapper<'a> { foo: &'a dyn MyTrait } impl <'a> Drop for MyWrapper<'a> { fn drop(&mut self) {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0158.md b/compiler/rustc_error_codes/src/error_codes/E0158.md index 03b93d925c19a..c31f1e13beee4 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0158.md +++ b/compiler/rustc_error_codes/src/error_codes/E0158.md @@ -1,5 +1,4 @@ -An associated `const`, `const` parameter or `static` has been referenced -in a pattern. +A generic parameter or `static` has been referenced in a pattern. Erroneous code example: @@ -15,25 +14,25 @@ trait Bar { fn test(arg: Foo) { match arg { - A::X => println!("A::X"), // error: E0158: associated consts cannot be - // referenced in patterns + A::X => println!("A::X"), // error: E0158: constant pattern depends + // on a generic parameter Foo::Two => println!("Two") } } ``` -Associated `const`s cannot be referenced in patterns because it is impossible +Generic parameters cannot be referenced in patterns because it is impossible for the compiler to prove exhaustiveness (that some pattern will always match). Take the above example, because Rust does type checking in the *generic* method, not the *monomorphized* specific instance. So because `Bar` could have -theoretically infinite implementations, there's no way to always be sure that +theoretically arbitrary implementations, there's no way to always be sure that `A::X` is `Foo::One`. So this code must be rejected. Even if code can be proven exhaustive by a programmer, the compiler cannot currently prove this. -The same holds true of `const` parameters and `static`s. +The same holds true of `static`s. -If you want to match against an associated `const`, `const` parameter or -`static` consider using a guard instead: +If you want to match against a `const` that depends on a generic parameter or a +`static`, consider using a guard instead: ``` trait Trait { diff --git a/compiler/rustc_error_codes/src/error_codes/E0798.md b/compiler/rustc_error_codes/src/error_codes/E0798.md new file mode 100644 index 0000000000000..da08cde301000 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0798.md @@ -0,0 +1,39 @@ +Functions marked as `C-cmse-nonsecure-call` place restrictions on their +inputs and outputs. + +- inputs must fit in the 4 available 32-bit argument registers. Alignment +is relevant. +- outputs must either fit in 4 bytes, or be a foundational type of +size 8 (`i64`, `u64`, `f64`). +- no generics can be used in the signature + +For more information, +see [arm's aapcs32](https://github.com/ARM-software/abi-aa/releases). + +Erroneous code example: + +```ignore (only fails on supported targets) +#![feature(abi_c_cmse_nonsecure_call)] + +#[no_mangle] +pub fn test( + f: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32, +) -> u32 { + f(1, 2, 3, 4, 5) +} +``` + +Arguments' alignment is respected. In the example below, padding is inserted +so that the `u64` argument is passed in registers r2 and r3. There is then no +room left for the final `f32` argument + +```ignore (only fails on supported targets) +#![feature(abi_c_cmse_nonsecure_call)] + +#[no_mangle] +pub fn test( + f: extern "C-cmse-nonsecure-call" fn(u32, u64, f32) -> u32, +) -> u32 { + f(1, 2, 3.0) +} +``` diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index d13d5e1bca219..2a7bc2501c081 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -536,6 +536,7 @@ E0794: 0794, E0795: 0795, E0796: 0796, E0797: 0797, +E0798: 0798, ); ) } diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index dbbd948fd7073..2964ac8cc5854 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -119,6 +119,8 @@ impl MetaVarExpr { } } +/// Indicates what is placed in a `concat` parameter. For example, literals +/// (`${concat("foo", "bar")}`) or adhoc identifiers (`${concat(foo, bar)}`). #[derive(Debug, Decodable, Encodable, PartialEq)] pub(crate) enum MetaVarExprConcatElem { /// Identifier WITHOUT a preceding dollar sign, which means that this identifier should be diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 9b4dc13c703a1..7e2ea8de5fca2 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -6,9 +6,10 @@ use crate::mbe::macro_parser::{NamedMatch, NamedMatch::*}; use crate::mbe::metavar_expr::{MetaVarExprConcatElem, RAW_IDENT_ERR}; use crate::mbe::{self, KleeneOp, MetaVarExpr}; use rustc_ast::mut_visit::{self, MutVisitor}; -use rustc_ast::token::IdentIsRaw; -use rustc_ast::token::{self, Delimiter, Token, TokenKind}; +use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind}; +use rustc_ast::token::{IdentIsRaw, Lit, LitKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; +use rustc_ast::ExprKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, Diag, DiagCtxtHandle, PResult}; use rustc_parse::lexer::nfc_normalize; @@ -17,7 +18,7 @@ use rustc_session::parse::ParseSess; use rustc_session::parse::SymbolGallery; use rustc_span::hygiene::{LocalExpnId, Transparency}; use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent}; -use rustc_span::{with_metavar_spans, Span, SyntaxContext}; +use rustc_span::{with_metavar_spans, Span, Symbol, SyntaxContext}; use smallvec::{smallvec, SmallVec}; use std::mem; @@ -691,12 +692,12 @@ fn transcribe_metavar_expr<'a>( MetaVarExpr::Concat(ref elements) => { let mut concatenated = String::new(); for element in elements.into_iter() { - let string = match element { - MetaVarExprConcatElem::Ident(elem) => elem.to_string(), - MetaVarExprConcatElem::Literal(elem) => elem.as_str().into(), - MetaVarExprConcatElem::Var(elem) => extract_ident(dcx, *elem, interp)?, + let symbol = match element { + MetaVarExprConcatElem::Ident(elem) => elem.name, + MetaVarExprConcatElem::Literal(elem) => *elem, + MetaVarExprConcatElem::Var(elem) => extract_var_symbol(dcx, *elem, interp)?, }; - concatenated.push_str(&string); + concatenated.push_str(symbol.as_str()); } let symbol = nfc_normalize(&concatenated); let concatenated_span = visited_span(); @@ -750,32 +751,42 @@ fn transcribe_metavar_expr<'a>( Ok(()) } -/// Extracts an identifier that can be originated from a `$var:ident` variable or from a token tree. -fn extract_ident<'a>( +/// Extracts an metavariable symbol that can be an identifier, a token tree or a literal. +fn extract_var_symbol<'a>( dcx: DiagCtxtHandle<'a>, ident: Ident, interp: &FxHashMap, -) -> PResult<'a, String> { +) -> PResult<'a, Symbol> { if let NamedMatch::MatchedSingle(pnr) = matched_from_ident(dcx, ident, interp)? { if let ParseNtResult::Ident(nt_ident, is_raw) = pnr { if let IdentIsRaw::Yes = is_raw { return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR)); } - return Ok(nt_ident.to_string()); + return Ok(nt_ident.name); } - if let ParseNtResult::Tt(TokenTree::Token( - Token { kind: TokenKind::Ident(token_ident, is_raw), .. }, - _, - )) = pnr - { - if let IdentIsRaw::Yes = is_raw { - return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR)); + + if let ParseNtResult::Tt(TokenTree::Token(Token { kind, .. }, _)) = pnr { + if let TokenKind::Ident(symbol, is_raw) = kind { + if let IdentIsRaw::Yes = is_raw { + return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR)); + } + return Ok(*symbol); } - return Ok(token_ident.to_string()); + + if let TokenKind::Literal(Lit { kind: LitKind::Str, symbol, suffix: None }) = kind { + return Ok(*symbol); + } + } + + if let ParseNtResult::Nt(nt) = pnr + && let Nonterminal::NtLiteral(expr) = &**nt + && let ExprKind::Lit(Lit { kind: LitKind::Str, symbol, suffix: None }) = &expr.kind + { + return Ok(*symbol); } } - Err(dcx.struct_span_err( - ident.span, - "`${concat(..)}` currently only accepts identifiers or meta-variables as parameters", - )) + Err(dcx + .struct_err("metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`") + .with_note("currently only string literals are supported") + .with_span(ident.span)) } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 3bd7b300758c4..bf773f2d48793 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -228,13 +228,53 @@ impl<'hir> PathSegment<'hir> { } } +/// A constant that enters the type system, used for arguments to const generics (e.g. array lengths). +/// +/// These are distinct from [`AnonConst`] as anon consts in the type system are not allowed +/// to use any generic parameters, therefore we must represent `N` differently. Additionally +/// future designs for supporting generic parameters in const arguments will likely not use +/// an anon const based design. +/// +/// So, `ConstArg` (specifically, [`ConstArgKind`]) distinguishes between const args +/// that are [just paths](ConstArgKind::Path) (currently just bare const params) +/// versus const args that are literals or have arbitrary computations (e.g., `{ 1 + 3 }`). #[derive(Clone, Copy, Debug, HashStable_Generic)] pub struct ConstArg<'hir> { - pub value: &'hir AnonConst, + #[stable_hasher(ignore)] + pub hir_id: HirId, + pub kind: ConstArgKind<'hir>, /// Indicates whether this comes from a `~const` desugaring. pub is_desugared_from_effects: bool, } +impl<'hir> ConstArg<'hir> { + pub fn anon_const_hir_id(&self) -> Option { + match self.kind { + ConstArgKind::Anon(ac) => Some(ac.hir_id), + _ => None, + } + } + + pub fn span(&self) -> Span { + match self.kind { + ConstArgKind::Path(path) => path.span(), + ConstArgKind::Anon(anon) => anon.span, + } + } +} + +/// See [`ConstArg`]. +#[derive(Clone, Copy, Debug, HashStable_Generic)] +pub enum ConstArgKind<'hir> { + /// **Note:** Currently this is only used for bare const params + /// (`N` where `fn foo(...)`), + /// not paths to any const (`N` where `const N: usize = ...`). + /// + /// However, in the future, we'll be using it for all of those. + Path(QPath<'hir>), + Anon(&'hir AnonConst), +} + #[derive(Clone, Copy, Debug, HashStable_Generic)] pub struct InferArg { pub hir_id: HirId, @@ -251,7 +291,7 @@ impl InferArg { pub enum GenericArg<'hir> { Lifetime(&'hir Lifetime), Type(&'hir Ty<'hir>), - Const(ConstArg<'hir>), + Const(&'hir ConstArg<'hir>), Infer(InferArg), } @@ -260,7 +300,7 @@ impl GenericArg<'_> { match self { GenericArg::Lifetime(l) => l.ident.span, GenericArg::Type(t) => t.span, - GenericArg::Const(c) => c.value.span, + GenericArg::Const(c) => c.span(), GenericArg::Infer(i) => i.span, } } @@ -269,7 +309,7 @@ impl GenericArg<'_> { match self { GenericArg::Lifetime(l) => l.hir_id, GenericArg::Type(t) => t.hir_id, - GenericArg::Const(c) => c.value.hir_id, + GenericArg::Const(c) => c.hir_id, GenericArg::Infer(i) => i.hir_id, } } @@ -524,7 +564,7 @@ pub enum GenericParamKind<'hir> { Const { ty: &'hir Ty<'hir>, /// Optional default value for the const generic param - default: Option<&'hir AnonConst>, + default: Option<&'hir ConstArg<'hir>>, is_host_effect: bool, synthetic: bool, }, @@ -1598,13 +1638,13 @@ pub type Lit = Spanned; #[derive(Copy, Clone, Debug, HashStable_Generic)] pub enum ArrayLen<'hir> { Infer(InferArg), - Body(&'hir AnonConst), + Body(&'hir ConstArg<'hir>), } impl ArrayLen<'_> { pub fn hir_id(&self) -> HirId { match self { - ArrayLen::Infer(InferArg { hir_id, .. }) | ArrayLen::Body(AnonConst { hir_id, .. }) => { + ArrayLen::Infer(InferArg { hir_id, .. }) | ArrayLen::Body(ConstArg { hir_id, .. }) => { *hir_id } } @@ -2434,7 +2474,7 @@ impl<'hir> AssocItemConstraint<'hir> { } /// Obtain the const on the RHS of an assoc const equality constraint if applicable. - pub fn ct(self) -> Option<&'hir AnonConst> { + pub fn ct(self) -> Option<&'hir ConstArg<'hir>> { match self.kind { AssocItemConstraintKind::Equality { term: Term::Const(ct) } => Some(ct), _ => None, @@ -2445,7 +2485,7 @@ impl<'hir> AssocItemConstraint<'hir> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum Term<'hir> { Ty(&'hir Ty<'hir>), - Const(&'hir AnonConst), + Const(&'hir ConstArg<'hir>), } impl<'hir> From<&'hir Ty<'hir>> for Term<'hir> { @@ -2454,8 +2494,8 @@ impl<'hir> From<&'hir Ty<'hir>> for Term<'hir> { } } -impl<'hir> From<&'hir AnonConst> for Term<'hir> { - fn from(c: &'hir AnonConst) -> Self { +impl<'hir> From<&'hir ConstArg<'hir>> for Term<'hir> { + fn from(c: &'hir ConstArg<'hir>) -> Self { Term::Const(c) } } @@ -3689,6 +3729,7 @@ pub enum Node<'hir> { Field(&'hir FieldDef<'hir>), AnonConst(&'hir AnonConst), ConstBlock(&'hir ConstBlock), + ConstArg(&'hir ConstArg<'hir>), Expr(&'hir Expr<'hir>), ExprField(&'hir ExprField<'hir>), Stmt(&'hir Stmt<'hir>), @@ -3750,6 +3791,7 @@ impl<'hir> Node<'hir> { Node::Param(..) | Node::AnonConst(..) | Node::ConstBlock(..) + | Node::ConstArg(..) | Node::Expr(..) | Node::Stmt(..) | Node::Block(..) @@ -3966,7 +4008,7 @@ mod size_asserts { static_assert_size!(FnDecl<'_>, 40); static_assert_size!(ForeignItem<'_>, 72); static_assert_size!(ForeignItemKind<'_>, 40); - static_assert_size!(GenericArg<'_>, 24); + static_assert_size!(GenericArg<'_>, 16); static_assert_size!(GenericBound<'_>, 48); static_assert_size!(Generics<'_>, 56); static_assert_size!(Impl<'_>, 80); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9bb3245ae05af..c202ee41e3132 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -347,6 +347,9 @@ pub trait Visitor<'v>: Sized { fn visit_inline_const(&mut self, c: &'v ConstBlock) -> Self::Result { walk_inline_const(self, c) } + fn visit_const_arg(&mut self, c: &'v ConstArg<'v>) -> Self::Result { + walk_const_arg(self, c) + } fn visit_expr(&mut self, ex: &'v Expr<'v>) -> Self::Result { walk_expr(self, ex) } @@ -364,7 +367,7 @@ pub trait Visitor<'v>: Sized { fn visit_generic_param(&mut self, p: &'v GenericParam<'v>) -> Self::Result { walk_generic_param(self, p) } - fn visit_const_param_default(&mut self, _param: HirId, ct: &'v AnonConst) -> Self::Result { + fn visit_const_param_default(&mut self, _param: HirId, ct: &'v ConstArg<'v>) -> Self::Result { walk_const_param_default(self, ct) } fn visit_generics(&mut self, g: &'v Generics<'v>) -> Self::Result { @@ -708,7 +711,7 @@ pub fn walk_array_len<'v, V: Visitor<'v>>(visitor: &mut V, len: &'v ArrayLen<'v> match len { // FIXME: Use `visit_infer` here. ArrayLen::Infer(InferArg { hir_id, span: _ }) => visitor.visit_id(*hir_id), - ArrayLen::Body(c) => visitor.visit_anon_const(c), + ArrayLen::Body(c) => visitor.visit_const_arg(c), } } @@ -725,6 +728,17 @@ pub fn walk_inline_const<'v, V: Visitor<'v>>( visitor.visit_nested_body(constant.body) } +pub fn walk_const_arg<'v, V: Visitor<'v>>( + visitor: &mut V, + const_arg: &'v ConstArg<'v>, +) -> V::Result { + try_visit!(visitor.visit_id(const_arg.hir_id)); + match &const_arg.kind { + ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, const_arg.hir_id, qpath.span()), + ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon), + } +} + pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) -> V::Result { try_visit!(visitor.visit_id(expression.hir_id)); match expression.kind { @@ -928,9 +942,9 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>( pub fn walk_const_param_default<'v, V: Visitor<'v>>( visitor: &mut V, - ct: &'v AnonConst, + ct: &'v ConstArg<'v>, ) -> V::Result { - visitor.visit_anon_const(ct) + visitor.visit_const_arg(ct) } pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics<'v>) -> V::Result { @@ -1216,7 +1230,7 @@ pub fn walk_generic_arg<'v, V: Visitor<'v>>( match generic_arg { GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt), GenericArg::Type(ty) => visitor.visit_ty(ty), - GenericArg::Const(ct) => visitor.visit_anon_const(&ct.value), + GenericArg::Const(ct) => visitor.visit_const_arg(ct), GenericArg::Infer(inf) => visitor.visit_infer(inf), } } @@ -1278,7 +1292,7 @@ pub fn walk_assoc_item_constraint<'v, V: Visitor<'v>>( match constraint.kind { AssocItemConstraintKind::Equality { ref term } => match term { Term::Ty(ref ty) => try_visit!(visitor.visit_ty(ty)), - Term::Const(ref c) => try_visit!(visitor.visit_anon_const(c)), + Term::Const(ref c) => try_visit!(visitor.visit_const_arg(c)), }, AssocItemConstraintKind::Bound { bounds } => { walk_list!(visitor, visit_param_bound, bounds) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 24c5377a3b125..f08a0f8c8fc19 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -58,6 +58,23 @@ hir_analysis_cannot_capture_late_bound_ty = hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are forbidden when `for<...>` is present .label = `for<...>` is here +hir_analysis_cmse_call_generic = + function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type + +hir_analysis_cmse_call_inputs_stack_spill = + arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers + .label = {$plural -> + [false] this argument doesn't + *[true] these arguments don't + } fit in the available registers + .note = functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers + +hir_analysis_cmse_call_output_stack_spill = + return value of `"C-cmse-nonsecure-call"` function too large to pass via registers + .label = this type doesn't fit in the available registers + .note1 = functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers + .note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size + hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions @@ -382,6 +399,10 @@ hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is no hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias .label = `Self` is not a generic argument, but an alias to the type of the {$what} +hir_analysis_recursive_generic_parameter = {$param_def_kind} `{$param_name}` is only used recursively + .label = {$param_def_kind} must be used non-recursively in the definition + .note = all type parameters must be used in a non-recursive way in order to constrain their variance + hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}` .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}` @@ -515,6 +536,11 @@ hir_analysis_typeof_reserved_keyword_used = .suggestion = consider replacing `typeof(...)` with an actual type .label = reserved keyword +hir_analysis_unconstrained_generic_parameter = the {$param_def_kind} `{$param_name}` is not constrained by the impl trait, self type, or predicates + .label = unconstrained {$param_def_kind} + .const_param_note = expressions using a const parameter must map each value to a distinct output value + .const_param_note2 = proving the result of expressions other than the parameter are unique is not supported + hir_analysis_unconstrained_opaque_type = unconstrained opaque type .note = `{$name}` must be used in combination with a concrete type within the same {$what} @@ -549,6 +575,8 @@ hir_analysis_unused_generic_parameter = {$param_def_kind} `{$param_name}` is never used .label = unused {$param_def_kind} .const_param_help = if you intended `{$param_name}` to be a const parameter, use `const {$param_name}: /* Type */` instead + .usage_spans = `{$param_name}` is named here, but is likely unused in the containing type + hir_analysis_unused_generic_parameter_adt_help = consider removing `{$param_name}`, referring to it in a field, or using a marker such as `{$phantom_data}` hir_analysis_unused_generic_parameter_adt_no_phantom_data_help = diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index bf8ef18c04fcc..dbc265ad3ff32 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1572,6 +1572,7 @@ fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalD param_name, param_def_kind: tcx.def_descr(param.def_id), help: errors::UnusedGenericParameterHelp::TyAlias { param_name }, + usage_spans: vec![], const_param_help, }); diag.code(E0091); diff --git a/compiler/rustc_hir_analysis/src/check/errs.rs b/compiler/rustc_hir_analysis/src/check/errs.rs index 17cb20df75463..19a0476e63016 100644 --- a/compiler/rustc_hir_analysis/src/check/errs.rs +++ b/compiler/rustc_hir_analysis/src/check/errs.rs @@ -1,5 +1,4 @@ use rustc_hir as hir; -use rustc_hir_pretty::qpath_to_string; use rustc_lint_defs::builtin::STATIC_MUT_REFS; use rustc_middle::ty::{Mutability, TyCtxt}; use rustc_span::Span; @@ -12,9 +11,17 @@ pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) { let hir_id = expr.hir_id; if let hir::ExprKind::AddrOf(borrow_kind, m, expr) = expr.kind && matches!(borrow_kind, hir::BorrowKind::Ref) - && let Some(var) = path_if_static_mut(tcx, expr) + && path_if_static_mut(expr) { - handle_static_mut_ref(tcx, span, var, span.edition().at_least_rust_2024(), m, hir_id); + handle_static_mut_ref( + tcx, + span, + span.with_hi(expr.span.lo()), + span.shrink_to_hi(), + span.edition().at_least_rust_2024(), + m, + hir_id, + ); } } @@ -24,12 +31,13 @@ pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) { && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind && let hir::ByRef::Yes(rmutbl) = ba.0 && let Some(init) = loc.init - && let Some(var) = path_if_static_mut(tcx, init) + && path_if_static_mut(init) { handle_static_mut_ref( tcx, init.span, - var, + init.span.shrink_to_lo(), + init.span.shrink_to_hi(), loc.span.edition().at_least_rust_2024(), rmutbl, stmt.hir_id, @@ -37,38 +45,39 @@ pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) { } } -fn path_if_static_mut(tcx: TyCtxt<'_>, expr: &hir::Expr<'_>) -> Option { +fn path_if_static_mut(expr: &hir::Expr<'_>) -> bool { if let hir::ExprKind::Path(qpath) = expr.kind && let hir::QPath::Resolved(_, path) = qpath && let hir::def::Res::Def(def_kind, _) = path.res && let hir::def::DefKind::Static { safety: _, mutability: Mutability::Mut, nested: false } = def_kind { - return Some(qpath_to_string(&tcx, &qpath)); + return true; } - None + false } fn handle_static_mut_ref( tcx: TyCtxt<'_>, span: Span, - var: String, + lo: Span, + hi: Span, e2024: bool, mutable: Mutability, hir_id: hir::HirId, ) { if e2024 { let (sugg, shared) = if mutable == Mutability::Mut { - (errors::StaticMutRefSugg::Mut { span, var }, "mutable") + (errors::MutRefSugg::Mut { lo, hi }, "mutable") } else { - (errors::StaticMutRefSugg::Shared { span, var }, "shared") + (errors::MutRefSugg::Shared { lo, hi }, "shared") }; tcx.dcx().emit_err(errors::StaticMutRef { span, sugg, shared }); } else { let (sugg, shared) = if mutable == Mutability::Mut { - (errors::RefOfMutStaticSugg::Mut { span, var }, "mutable") + (errors::MutRefSugg::Mut { lo, hi }, "mutable") } else { - (errors::RefOfMutStaticSugg::Shared { span, var }, "shared") + (errors::MutRefSugg::Shared { lo, hi }, "shared") }; tcx.emit_node_span_lint( STATIC_MUT_REFS, diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 6a36938dd1d51..24aeb02446194 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -82,7 +82,7 @@ use rustc_errors::{pluralize, struct_span_code_err, Diag}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_index::bit_set::BitSet; -use rustc_infer::infer::error_reporting::ObligationCauseExt as _; +use rustc_infer::error_reporting::infer::ObligationCauseExt as _; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, TyCtxtInferExt as _}; use rustc_infer::traits::ObligationCause; @@ -166,21 +166,42 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { return; } - // For the wasm32 target statics with `#[link_section]` are placed into custom - // sections of the final output file, but this isn't link custom sections of - // other executable formats. Namely we can only embed a list of bytes, - // nothing with provenance (pointers to anything else). If any provenance - // show up, reject it here. + // For the wasm32 target statics with `#[link_section]` other than `.init_array` + // are placed into custom sections of the final output file, but this isn't like + // custom sections of other executable formats. Namely we can only embed a list + // of bytes, nothing with provenance (pointers to anything else). If any + // provenance show up, reject it here. // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is // the consumer's responsibility to ensure all bytes that have been read // have defined values. + // + // The `.init_array` section is left to go through the normal custom section code path. + // When dealing with `.init_array` wasm-ld currently has several limitations. This manifests + // in workarounds in user-code. + // + // * The linker fails to merge multiple items in a crate into the .init_array section. + // To work around this, a single array can be used placing multiple items in the array. + // #[link_section = ".init_array"] + // static FOO: [unsafe extern "C" fn(); 2] = [ctor, ctor]; + // * Even symbols marked used get gc'd from dependant crates unless at least one symbol + // in the crate is marked with an `#[export_name]` + // + // Once `.init_array` support in wasm-ld is complete, the user code workarounds should + // continue to work, but would no longer be necessary. + if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id()) && alloc.inner().provenance().ptrs().len() != 0 { - let msg = "statics with a custom `#[link_section]` must be a \ + if attrs + .link_section + .map(|link_section| !link_section.as_str().starts_with(".init_array")) + .unwrap() + { + let msg = "statics with a custom `#[link_section]` must be a \ simple list of bytes on the wasm target with no \ extra levels of indirection such as references"; - tcx.dcx().span_err(tcx.def_span(id), msg); + tcx.dcx().span_err(tcx.def_span(id), msg); + } } } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index b2ef07d65c5b4..71a7b0b16389b 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -4,12 +4,12 @@ use crate::constrained_generic_params::{identify_constrained_generic_params, Par use crate::errors; use crate::fluent_generated as fluent; -use hir::intravisit::Visitor; +use hir::intravisit::{self, Visitor}; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_hir::def::DefKind; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::ItemKind; @@ -1799,7 +1799,7 @@ fn receiver_is_implemented<'tcx>( fn check_variances_for_type_defn<'tcx>( tcx: TyCtxt<'tcx>, - item: &hir::Item<'tcx>, + item: &'tcx hir::Item<'tcx>, hir_generics: &hir::Generics<'tcx>, ) { let identity_args = ty::GenericArgs::identity_for_item(tcx, item.owner_id); @@ -1886,21 +1886,21 @@ fn check_variances_for_type_defn<'tcx>( hir::ParamName::Error => {} _ => { let has_explicit_bounds = explicitly_bounded_params.contains(¶meter); - report_bivariance(tcx, hir_param, has_explicit_bounds, item.kind); + report_bivariance(tcx, hir_param, has_explicit_bounds, item); } } } } -fn report_bivariance( - tcx: TyCtxt<'_>, - param: &rustc_hir::GenericParam<'_>, +fn report_bivariance<'tcx>( + tcx: TyCtxt<'tcx>, + param: &'tcx hir::GenericParam<'tcx>, has_explicit_bounds: bool, - item_kind: ItemKind<'_>, + item: &'tcx hir::Item<'tcx>, ) -> ErrorGuaranteed { let param_name = param.name.ident(); - let help = match item_kind { + let help = match item.kind { ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => { if let Some(def_id) = tcx.lang_items().phantom_data() { errors::UnusedGenericParameterHelp::Adt { @@ -1915,6 +1915,41 @@ fn report_bivariance( item_kind => bug!("report_bivariance: unexpected item kind: {item_kind:?}"), }; + let mut usage_spans = vec![]; + intravisit::walk_item( + &mut CollectUsageSpans { spans: &mut usage_spans, param_def_id: param.def_id.to_def_id() }, + item, + ); + + if !usage_spans.is_empty() { + // First, check if the ADT/LTA is (probably) cyclical. We say probably here, since we're + // not actually looking into substitutions, just walking through fields / the "RHS". + // We don't recurse into the hidden types of opaques or anything else fancy. + let item_def_id = item.owner_id.to_def_id(); + let is_probably_cyclical = + IsProbablyCyclical { tcx, item_def_id, seen: Default::default() } + .visit_def(item_def_id) + .is_break(); + // If the ADT/LTA is cyclical, then if at least one usage of the type parameter or + // the `Self` alias is present in the, then it's probably a cyclical struct/ type + // alias, and we should call those parameter usages recursive rather than just saying + // they're unused... + // + // We currently report *all* of the parameter usages, since computing the exact + // subset is very involved, and the fact we're mentioning recursion at all is + // likely to guide the user in the right direction. + if is_probably_cyclical { + return tcx.dcx().emit_err(errors::RecursiveGenericParameter { + spans: usage_spans, + param_span: param.span, + param_name, + param_def_kind: tcx.def_descr(param.def_id.to_def_id()), + help, + note: (), + }); + } + } + let const_param_help = matches!(param.kind, hir::GenericParamKind::Type { .. } if !has_explicit_bounds) .then_some(()); @@ -1923,6 +1958,7 @@ fn report_bivariance( span: param.span, param_name, param_def_kind: tcx.def_descr(param.def_id.to_def_id()), + usage_spans, help, const_param_help, }); @@ -1930,6 +1966,86 @@ fn report_bivariance( diag.emit() } +/// Detects cases where an ADT/LTA is trivially cyclical -- we want to detect this so +/// we only mention that its parameters are used cyclically if the ADT/LTA is truly +/// cyclical. +/// +/// Notably, we don't consider substitutions here, so this may have false positives. +struct IsProbablyCyclical<'tcx> { + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + seen: FxHashSet, +} + +impl<'tcx> IsProbablyCyclical<'tcx> { + fn visit_def(&mut self, def_id: DefId) -> ControlFlow<(), ()> { + match self.tcx.def_kind(def_id) { + DefKind::Struct | DefKind::Enum | DefKind::Union => { + self.tcx.adt_def(def_id).all_fields().try_for_each(|field| { + self.tcx.type_of(field.did).instantiate_identity().visit_with(self) + }) + } + DefKind::TyAlias if self.tcx.type_alias_is_lazy(def_id) => { + self.tcx.type_of(def_id).instantiate_identity().visit_with(self) + } + _ => ControlFlow::Continue(()), + } + } +} + +impl<'tcx> TypeVisitor> for IsProbablyCyclical<'tcx> { + type Result = ControlFlow<(), ()>; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<(), ()> { + let def_id = match ty.kind() { + ty::Adt(adt_def, _) => Some(adt_def.did()), + ty::Alias(ty::Weak, alias_ty) => Some(alias_ty.def_id), + _ => None, + }; + if let Some(def_id) = def_id { + if def_id == self.item_def_id { + return ControlFlow::Break(()); + } + if self.seen.insert(def_id) { + self.visit_def(def_id)?; + } + } + ty.super_visit_with(self) + } +} + +/// Collect usages of the `param_def_id` and `Res::SelfTyAlias` in the HIR. +/// +/// This is used to report places where the user has used parameters in a +/// non-variance-constraining way for better bivariance errors. +struct CollectUsageSpans<'a> { + spans: &'a mut Vec, + param_def_id: DefId, +} + +impl<'tcx> Visitor<'tcx> for CollectUsageSpans<'_> { + type Result = (); + + fn visit_generics(&mut self, _g: &'tcx rustc_hir::Generics<'tcx>) -> Self::Result { + // Skip the generics. We only care about fields, not where clause/param bounds. + } + + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) -> Self::Result { + if let hir::TyKind::Path(hir::QPath::Resolved(None, qpath)) = t.kind { + if let Res::Def(DefKind::TyParam, def_id) = qpath.res + && def_id == self.param_def_id + { + self.spans.push(t.span); + return; + } else if let Res::SelfTyAlias { .. } = qpath.res { + self.spans.push(t.span); + return; + } + } + intravisit::walk_ty(self, t); + } +} + impl<'tcx> WfCheckingCtxt<'_, 'tcx> { /// Feature gates RFC 2056 -- trivial bounds, checking for global bounds that /// aren't true. diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index e0aad29916323..5e23d47327408 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -304,7 +304,9 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { self.tcx.ensure().type_of(param.def_id); if let Some(default) = default { // need to store default and type of default - self.tcx.ensure().type_of(default.def_id); + if let hir::ConstArgKind::Anon(ac) = default.kind { + self.tcx.ensure().type_of(ac.def_id); + } self.tcx.ensure().const_param_default(param.def_id); } } diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 22d465c8e62be..690423421b975 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -13,7 +13,7 @@ use rustc_session::lint; use rustc_span::symbol::{kw, Symbol}; use rustc_span::Span; -#[instrument(level = "debug", skip(tcx))] +#[instrument(level = "debug", skip(tcx), ret)] pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { use rustc_hir::*; @@ -102,6 +102,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { None } else if tcx.features().generic_const_exprs { let parent_node = tcx.parent_hir_node(hir_id); + debug!(?parent_node); if let Node::Variant(Variant { disr_expr: Some(constant), .. }) = parent_node && constant.hir_id == hir_id { @@ -164,13 +165,17 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { } } else { let parent_node = tcx.parent_hir_node(hir_id); + let parent_node = match parent_node { + Node::ConstArg(ca) => tcx.parent_hir_node(ca.hir_id), + _ => parent_node, + }; match parent_node { // HACK(eddyb) this provides the correct generics for repeat // expressions' count (i.e. `N` in `[x; N]`), and explicit // `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`), // as they shouldn't be able to cause query cycle errors. - Node::Expr(Expr { kind: ExprKind::Repeat(_, constant), .. }) - if constant.hir_id() == hir_id => + Node::Expr(Expr { kind: ExprKind::Repeat(_, ArrayLen::Body(ct)), .. }) + if ct.anon_const_hir_id() == Some(hir_id) => { Some(parent_did) } diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index b89d034fc2e3f..9e430c83e20d7 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -388,7 +388,7 @@ fn const_evaluatable_predicates_of( } } - fn visit_const_param_default(&mut self, _param: HirId, _ct: &'tcx hir::AnonConst) { + fn visit_const_param_default(&mut self, _param: HirId, _ct: &'tcx hir::ConstArg<'tcx>) { // Do not look into const param defaults, // these get checked when they are actually instantiated. // 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 cad7870a0a1d8..7930f54038daf 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -954,7 +954,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { GenericParamKind::Const { ty, default, .. } => { self.visit_ty(ty); if let Some(default) = default { - self.visit_body(self.tcx.hir().body(default.body)); + self.visit_const_arg(default); } } } @@ -1594,7 +1594,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { i += 1; } GenericArg::Const(ct) => { - self.visit_anon_const(&ct.value); + self.visit_const_arg(ct); i += 1; } GenericArg::Infer(inf) => { diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 974dd415f464c..9affd654366f1 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -35,16 +35,32 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { 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 { - // Easy case: arrays repeat expressions. - Node::Ty(&hir::Ty { kind: TyKind::Array(_, ref constant), .. }) - | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) - if constant.hir_id() == hir_id => + match parent_node { + // Anon consts "inside" the type system. + Node::ConstArg(&ConstArg { + hir_id: arg_hir_id, + kind: ConstArgKind::Anon(&AnonConst { hir_id: anon_hir_id, .. }), + .. + }) if anon_hir_id == hir_id => const_arg_anon_type_of(tcx, arg_hir_id, span), + + // Anon consts outside the type system. + Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) + | Node::Item(&Item { kind: ItemKind::GlobalAsm(asm), .. }) + if asm.operands.iter().any(|(op, _op_sp)| match op { + hir::InlineAsmOperand::Const { anon_const } + | hir::InlineAsmOperand::SymFn { anon_const } => anon_const.hir_id == hir_id, + _ => false, + }) => { - return tcx.types.usize; + tcx.typeck(def_id).node_type(hir_id) + } + Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => { + tcx.adt_def(tcx.hir().get_parent_item(hir_id)).repr().discr_type().to_ty(tcx) } + // Sort of affects the type system, but only for the purpose of diagnostics + // so no need for ConstArg. Node::Ty(&hir::Ty { kind: TyKind::Typeof(ref e), span, .. }) if e.hir_id == hir_id => { - let ty = tcx.typeck(def_id).node_type(e.hir_id); + let ty = tcx.typeck(def_id).node_type(tcx.local_def_id_to_hir_id(def_id)); let ty = tcx.fold_regions(ty, |r, _| { if r.is_erased() { ty::Region::new_error_misc(tcx) } else { r } }); @@ -56,24 +72,35 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { tcx.dcx().emit_err(TypeofReservedKeywordUsed { span, ty, opt_sugg }); return ty; } - Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) - | Node::Item(&Item { kind: ItemKind::GlobalAsm(asm), .. }) - if asm.operands.iter().any(|(op, _op_sp)| match op { - hir::InlineAsmOperand::Const { anon_const } - | hir::InlineAsmOperand::SymFn { anon_const } => anon_const.hir_id == hir_id, - _ => false, - }) => + + _ => Ty::new_error_with_message( + tcx, + span, + format!("unexpected anon const parent in type_of(): {parent_node:?}"), + ), + } +} + +fn const_arg_anon_type_of<'tcx>(tcx: TyCtxt<'tcx>, arg_hir_id: HirId, span: Span) -> Ty<'tcx> { + use hir::*; + use rustc_middle::ty::Ty; + + let parent_node_id = tcx.parent_hir_id(arg_hir_id); + let parent_node = tcx.hir_node(parent_node_id); + + let (generics, arg_idx) = match parent_node { + // Easy case: arrays repeat expressions. + Node::Ty(&hir::Ty { kind: TyKind::Array(_, ref constant), .. }) + | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) + if constant.hir_id() == arg_hir_id => { - return tcx.typeck(def_id).node_type(hir_id); - } - Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => { - return tcx.adt_def(tcx.hir().get_parent_item(hir_id)).repr().discr_type().to_ty(tcx); + return tcx.types.usize; } Node::GenericParam(&GenericParam { def_id: param_def_id, kind: GenericParamKind::Const { default: Some(ct), .. }, .. - }) if ct.hir_id == hir_id => { + }) if ct.hir_id == arg_hir_id => { return tcx .type_of(param_def_id) .no_bound_vars() @@ -104,7 +131,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { // to a ty::Alias(ty::Projection, `::Assoc<3>`). let item_def_id = tcx .hir() - .parent_owner_iter(hir_id) + .parent_owner_iter(arg_hir_id) .find(|(_, node)| matches!(node, OwnerNode::Item(_))) .unwrap() .0 @@ -124,7 +151,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { args.args .iter() .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.hir_id() == hir_id) + .position(|arg| arg.hir_id() == arg_hir_id) }) .unwrap_or_else(|| { bug!("no arg matching AnonConst in segment"); @@ -145,7 +172,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)), .. }) => { - let body_owner = tcx.hir().enclosing_body_owner(hir_id); + let body_owner = tcx.hir().enclosing_body_owner(arg_hir_id); let tables = tcx.typeck(body_owner); // This may fail in case the method/path does not actually exist. // As there is no relevant param for `def_id`, we simply return @@ -163,10 +190,10 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { args.args .iter() .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.hir_id() == hir_id) + .position(|arg| arg.hir_id() == arg_hir_id) }) .unwrap_or_else(|| { - bug!("no arg matching AnonConst in segment"); + bug!("no arg matching ConstArg in segment"); }); (tcx.generics_of(type_dependent_def), idx) @@ -185,18 +212,18 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { | ExprKind::Struct(&QPath::Resolved(_, path), ..), .. }) => { - let body_owner = tcx.hir().enclosing_body_owner(hir_id); + let body_owner = tcx.hir().enclosing_body_owner(arg_hir_id); let _tables = tcx.typeck(body_owner); &*path } Node::Pat(pat) => { - if let Some(path) = get_path_containing_arg_in_pat(pat, hir_id) { + if let Some(path) = get_path_containing_arg_in_pat(pat, arg_hir_id) { path } else { return Ty::new_error_with_message( tcx, span, - format!("unable to find const parent for {hir_id} in pat {pat:?}"), + format!("unable to find const parent for {arg_hir_id} in pat {pat:?}"), ); } } @@ -217,14 +244,14 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { args.args .iter() .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.hir_id() == hir_id) + .position(|arg| arg.hir_id() == arg_hir_id) .map(|index| (index, seg)) .or_else(|| { args.constraints .iter() .copied() .filter_map(AssocItemConstraint::ct) - .position(|ct| ct.hir_id == hir_id) + .position(|ct| ct.hir_id == arg_hir_id) .map(|idx| (idx, seg)) }) }) else { @@ -249,7 +276,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { return Ty::new_error_with_message( tcx, span, - format!("unexpected const parent in type_of(): {parent_node:?}"), + format!("unexpected const arg parent in type_of(): {parent_node:?}"), ); } }; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 0ee87a13e9e37..2eca64c27d087 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1500,33 +1500,33 @@ pub struct StaticMutRef<'a> { #[label] pub span: Span, #[subdiagnostic] - pub sugg: StaticMutRefSugg, + pub sugg: MutRefSugg, pub shared: &'a str, } #[derive(Subdiagnostic)] -pub enum StaticMutRefSugg { - #[suggestion( +pub enum MutRefSugg { + #[multipart_suggestion( hir_analysis_suggestion, style = "verbose", - code = "addr_of!({var})", applicability = "maybe-incorrect" )] Shared { - #[primary_span] - span: Span, - var: String, + #[suggestion_part(code = "addr_of!(")] + lo: Span, + #[suggestion_part(code = ")")] + hi: Span, }, - #[suggestion( + #[multipart_suggestion( hir_analysis_suggestion_mut, style = "verbose", - code = "addr_of_mut!({var})", applicability = "maybe-incorrect" )] Mut { - #[primary_span] - span: Span, - var: String, + #[suggestion_part(code = "addr_of_mut!(")] + lo: Span, + #[suggestion_part(code = ")")] + hi: Span, }, } @@ -1539,36 +1539,10 @@ pub struct RefOfMutStatic<'a> { #[label] pub span: Span, #[subdiagnostic] - pub sugg: RefOfMutStaticSugg, + pub sugg: MutRefSugg, pub shared: &'a str, } -#[derive(Subdiagnostic)] -pub enum RefOfMutStaticSugg { - #[suggestion( - hir_analysis_suggestion, - style = "verbose", - code = "addr_of!({var})", - applicability = "maybe-incorrect" - )] - Shared { - #[primary_span] - span: Span, - var: String, - }, - #[suggestion( - hir_analysis_suggestion_mut, - style = "verbose", - code = "addr_of_mut!({var})", - applicability = "maybe-incorrect" - )] - Mut { - #[primary_span] - span: Span, - var: String, - }, -} - #[derive(Diagnostic)] #[diag(hir_analysis_not_supported_delegation)] pub struct NotSupportedDelegation<'a> { @@ -1597,12 +1571,29 @@ pub(crate) struct UnusedGenericParameter { pub span: Span, pub param_name: Ident, pub param_def_kind: &'static str, + #[label(hir_analysis_usage_spans)] + pub usage_spans: Vec, #[subdiagnostic] pub help: UnusedGenericParameterHelp, #[help(hir_analysis_const_param_help)] pub const_param_help: Option<()>, } +#[derive(Diagnostic)] +#[diag(hir_analysis_recursive_generic_parameter)] +pub(crate) struct RecursiveGenericParameter { + #[primary_span] + pub spans: Vec, + #[label] + pub param_span: Span, + pub param_name: Ident, + pub param_def_kind: &'static str, + #[subdiagnostic] + pub help: UnusedGenericParameterHelp, + #[note] + pub note: (), +} + #[derive(Subdiagnostic)] pub(crate) enum UnusedGenericParameterHelp { #[help(hir_analysis_unused_generic_parameter_adt_help)] @@ -1613,6 +1604,20 @@ pub(crate) enum UnusedGenericParameterHelp { TyAlias { param_name: Ident }, } +#[derive(Diagnostic)] +#[diag(hir_analysis_unconstrained_generic_parameter)] +pub(crate) struct UnconstrainedGenericParameter { + #[primary_span] + #[label] + pub span: Span, + pub param_name: Symbol, + pub param_def_kind: &'static str, + #[note(hir_analysis_const_param_note)] + pub const_param_note: Option<()>, + #[note(hir_analysis_const_param_note2)] + pub const_param_note2: Option<()>, +} + #[derive(Diagnostic)] pub enum UnnamedFieldsRepr<'a> { #[diag(hir_analysis_unnamed_fields_repr_missing_repr_c)] @@ -1682,3 +1687,30 @@ pub struct InvalidReceiverTy<'tcx> { #[note] #[help] pub struct EffectsWithoutNextSolver; + +#[derive(Diagnostic)] +#[diag(hir_analysis_cmse_call_inputs_stack_spill, code = E0798)] +#[note] +pub struct CmseCallInputsStackSpill { + #[primary_span] + #[label] + pub span: Span, + pub plural: bool, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_cmse_call_output_stack_spill, code = E0798)] +#[note(hir_analysis_note1)] +#[note(hir_analysis_note2)] +pub struct CmseCallOutputStackSpill { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_cmse_call_generic, code = E0798)] +pub struct CmseCallGeneric { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index a1feef9e15b7e..6f9c481650b21 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -413,12 +413,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }); // Provide the resolved type of the associated constant to `type_of(AnonConst)`. - if let Some(anon_const) = constraint.ct() { - let ty = alias_term - .map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args)); - let ty = - check_assoc_const_binding_type(self, constraint.ident, ty, constraint.hir_id); - tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty)); + if let Some(const_arg) = constraint.ct() { + if let hir::ConstArgKind::Anon(anon_const) = const_arg.kind { + let ty = alias_term + .map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args)); + let ty = check_assoc_const_binding_type( + self, + constraint.ident, + ty, + constraint.hir_id, + ); + tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty)); + } } alias_term @@ -435,7 +441,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::AssocItemConstraintKind::Equality { term } => { let term = match term { hir::Term::Ty(ty) => self.lower_ty(ty).into(), - hir::Term::Const(ct) => ty::Const::from_anon_const(tcx, ct.def_id).into(), + hir::Term::Const(ct) => { + ty::Const::from_const_arg(tcx, ct, ty::FeedConstTy::No).into() + } }; // Find any late-bound regions declared in `ty` that are not diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs new file mode 100644 index 0000000000000..e99717ce00f8c --- /dev/null +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -0,0 +1,156 @@ +use rustc_errors::DiagCtxtHandle; +use rustc_hir as hir; +use rustc_hir::HirId; +use rustc_middle::ty::layout::LayoutError; +use rustc_middle::ty::{self, ParamEnv, TyCtxt}; +use rustc_span::Span; +use rustc_target::spec::abi; + +use crate::errors; + +/// Check conditions on inputs and outputs that the cmse ABIs impose: arguments and results MUST be +/// returned via registers (i.e. MUST NOT spill to the stack). LLVM will also validate these +/// conditions, but by checking them here rustc can emit nicer error messages. +pub fn validate_cmse_abi<'tcx>( + tcx: TyCtxt<'tcx>, + dcx: DiagCtxtHandle<'_>, + hir_id: HirId, + abi: abi::Abi, + fn_sig: ty::PolyFnSig<'tcx>, +) { + if let abi::Abi::CCmseNonSecureCall = abi { + let hir_node = tcx.hir_node(hir_id); + let hir::Node::Ty(hir::Ty { + span: bare_fn_span, + kind: hir::TyKind::BareFn(bare_fn_ty), + .. + }) = hir_node + else { + // might happen when this ABI is used incorrectly. That will be handled elsewhere + return; + }; + + match is_valid_cmse_inputs(tcx, fn_sig) { + Ok(Ok(())) => {} + Ok(Err(index)) => { + // fn(x: u32, u32, u32, u16, y: u16) -> u32, + // ^^^^^^ + let span = bare_fn_ty.param_names[index] + .span + .to(bare_fn_ty.decl.inputs[index].span) + .to(bare_fn_ty.decl.inputs.last().unwrap().span); + let plural = bare_fn_ty.param_names.len() - index != 1; + dcx.emit_err(errors::CmseCallInputsStackSpill { span, plural }); + } + Err(layout_err) => { + if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) { + dcx.emit_err(err); + } + } + } + + match is_valid_cmse_output(tcx, fn_sig) { + Ok(true) => {} + Ok(false) => { + let span = bare_fn_ty.decl.output.span(); + dcx.emit_err(errors::CmseCallOutputStackSpill { span }); + } + Err(layout_err) => { + if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) { + dcx.emit_err(err); + } + } + }; + } +} + +/// Returns whether the inputs will fit into the available registers +fn is_valid_cmse_inputs<'tcx>( + tcx: TyCtxt<'tcx>, + fn_sig: ty::PolyFnSig<'tcx>, +) -> Result, &'tcx LayoutError<'tcx>> { + let mut span = None; + let mut accum = 0u64; + + for (index, arg_def) in fn_sig.inputs().iter().enumerate() { + let layout = tcx.layout_of(ParamEnv::reveal_all().and(*arg_def.skip_binder()))?; + + let align = layout.layout.align().abi.bytes(); + let size = layout.layout.size().bytes(); + + accum += size; + accum = accum.next_multiple_of(Ord::max(4, align)); + + // i.e. exceeds 4 32-bit registers + if accum > 16 { + span = span.or(Some(index)); + } + } + + match span { + None => Ok(Ok(())), + Some(span) => Ok(Err(span)), + } +} + +/// Returns whether the output will fit into the available registers +fn is_valid_cmse_output<'tcx>( + tcx: TyCtxt<'tcx>, + fn_sig: ty::PolyFnSig<'tcx>, +) -> Result> { + let mut ret_ty = fn_sig.output().skip_binder(); + let layout = tcx.layout_of(ParamEnv::reveal_all().and(ret_ty))?; + let size = layout.layout.size().bytes(); + + if size <= 4 { + return Ok(true); + } else if size > 8 { + return Ok(false); + } + + // next we need to peel any repr(transparent) layers off + 'outer: loop { + let ty::Adt(adt_def, args) = ret_ty.kind() else { + break; + }; + + if !adt_def.repr().transparent() { + break; + } + + // the first field with non-trivial size and alignment must be the data + for variant_def in adt_def.variants() { + for field_def in variant_def.fields.iter() { + let ty = field_def.ty(tcx, args); + let layout = tcx.layout_of(ParamEnv::reveal_all().and(ty))?; + + if !layout.layout.is_1zst() { + ret_ty = ty; + continue 'outer; + } + } + } + } + + Ok(ret_ty == tcx.types.i64 || ret_ty == tcx.types.u64 || ret_ty == tcx.types.f64) +} + +fn cmse_layout_err<'tcx>( + layout_err: &'tcx LayoutError<'tcx>, + span: Span, +) -> Option { + use LayoutError::*; + + match layout_err { + Unknown(ty) => { + if ty.is_impl_trait() { + None // prevent double reporting of this error + } else { + Some(errors::CmseCallGeneric { span }) + } + } + SizeOverflow(..) | NormalizationFailure(..) | ReferencesError(..) | Cycle(..) => { + None // not our job to report these + } + } +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 5e595488ea7a8..8ff6ced8b3983 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -340,7 +340,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { { let span = match term { hir::Term::Ty(ty) => ty.span, - hir::Term::Const(ct) => tcx.def_span(ct.def_id), + hir::Term::Const(ct) => ct.span(), }; (span, Some(ident.span), assoc_item.kind, assoc_kind) } else { @@ -1257,14 +1257,12 @@ pub fn prohibit_assoc_item_constraint( }; // Now emit the suggestion - if let Ok(suggestion) = tcx.sess.source_map().span_to_snippet(removal_span) { - e.span_suggestion_verbose( - removal_span, - format!("consider removing this associated item {}", constraint.kind.descr()), - suggestion, - Applicability::MaybeIncorrect, - ); - } + e.span_suggestion_verbose( + removal_span, + format!("consider removing this associated item {}", constraint.kind.descr()), + "", + Applicability::MaybeIncorrect, + ); }; // Suggest replacing the associated item binding with a generic argument. @@ -1296,8 +1294,7 @@ pub fn prohibit_assoc_item_constraint( hir::AssocItemConstraintKind::Equality { term: hir::Term::Const(c) }, GenericParamDefKind::Const { .. }, ) => { - let span = tcx.hir().span(c.hir_id); - suggest_direct_use(&mut err, span); + suggest_direct_use(&mut err, c.span()); } (hir::AssocItemConstraintKind::Bound { bounds }, _) => { // Suggest `impl Trait for Foo` when finding @@ -1340,11 +1337,13 @@ pub fn prohibit_assoc_item_constraint( format!("<{lifetimes}{type_with_constraints}>"), ) }; - let suggestions = - vec![param_decl, (constraint.span, format!("{}", matching_param.name))]; + let suggestions = vec![ + param_decl, + (constraint.span.with_lo(constraint.ident.span.hi()), String::new()), + ]; err.multipart_suggestion_verbose( - format!("declare the type parameter right after the `impl` keyword"), + "declare the type parameter right after the `impl` keyword", suggestions, Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index b1c77db9f3700..abe2cff321f76 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -113,8 +113,12 @@ fn generic_arg_mismatch_err( } } (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => { - let body = tcx.hir().body(cnst.value.body); - if let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body.value.kind + // FIXME(min_generic_const_args): once ConstArgKind::Path is used for non-params too, + // this should match against that instead of ::Anon + if let hir::ConstArgKind::Anon(anon) = cnst.kind + && let body = tcx.hir().body(anon.body) + && let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = + body.value.kind { if let Res::Def(DefKind::Fn { .. }, id) = path.res { err.help(format!("`{}` is a function item, not a type", tcx.item_name(id))); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index a665306f2c6a8..d6eb1a66902fc 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -14,6 +14,7 @@ //! trait references and bounds. mod bounds; +mod cmse; pub mod errors; pub mod generics; mod lint; @@ -471,11 +472,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { handle_ty_args(has_default, &inf.to_ty()) } (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - let did = ct.value.def_id; - tcx.feed_anon_const_type(did, tcx.type_of(param.def_id)); - ty::Const::from_anon_const(tcx, did).into() + ty::Const::from_const_arg(tcx, ct, ty::FeedConstTy::Param(param.def_id)) + .into() } - (&GenericParamDefKind::Const { .. }, hir::GenericArg::Infer(inf)) => { + (&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { self.lowerer.ct_infer(Some(param), inf.span).into() } (kind, arg) => span_bug!( @@ -912,7 +912,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let term: ty::Term<'_> = match term { hir::Term::Ty(ty) => self.lower_ty(ty).into(), hir::Term::Const(ct) => { - ty::Const::from_anon_const(tcx, ct.def_id).into() + ty::Const::from_const_arg(tcx, ct, ty::FeedConstTy::No) + .into() } }; // FIXME(#97583): This isn't syntactically well-formed! @@ -1087,7 +1088,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT"); - if let Some(suggested_name) = find_best_match_for_name( + if let Some(variant_name) = find_best_match_for_name( &adt_def .variants() .iter() @@ -1095,12 +1096,66 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .collect::>(), assoc_ident.name, None, - ) { - err.span_suggestion( - assoc_ident.span, + ) && let Some(variant) = + adt_def.variants().iter().find(|s| s.name == variant_name) + { + let mut suggestion = vec![(assoc_ident.span, variant_name.to_string())]; + if let hir::Node::Stmt(hir::Stmt { + kind: hir::StmtKind::Semi(ref expr), + .. + }) + | hir::Node::Expr(ref expr) = tcx.parent_hir_node(hir_ref_id) + && let hir::ExprKind::Struct(..) = expr.kind + { + match variant.ctor { + None => { + // struct + suggestion = vec![( + assoc_ident.span.with_hi(expr.span.hi()), + if variant.fields.is_empty() { + format!("{variant_name} {{}}") + } else { + format!( + "{variant_name} {{ {} }}", + variant + .fields + .iter() + .map(|f| format!("{}: /* value */", f.name)) + .collect::>() + .join(", ") + ) + }, + )]; + } + Some((hir::def::CtorKind::Fn, def_id)) => { + // tuple + let fn_sig = tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + suggestion = vec![( + assoc_ident.span.with_hi(expr.span.hi()), + format!( + "{variant_name}({})", + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", ") + ), + )]; + } + Some((hir::def::CtorKind::Const, _)) => { + // unit + suggestion = vec![( + assoc_ident.span.with_hi(expr.span.hi()), + variant_name.to_string(), + )]; + } + } + } + err.multipart_suggestion_verbose( "there is a variant with a similar name", - suggested_name, - Applicability::MaybeIncorrect, + suggestion, + Applicability::HasPlaceholders, ); } else { err.span_label( @@ -2140,7 +2195,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let length = match length { hir::ArrayLen::Infer(inf) => self.ct_infer(None, inf.span), hir::ArrayLen::Body(constant) => { - ty::Const::from_anon_const(tcx, constant.def_id) + ty::Const::from_const_arg(tcx, constant, ty::FeedConstTy::No) } }; @@ -2324,6 +2379,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, safety, abi); let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); + // reject function types that violate cmse ABI requirements + cmse::validate_cmse_abi(self.tcx(), self.dcx(), hir_id, abi, bare_fn_ty); + // Find any late-bound regions declared in return type that do // not appear in the arguments. These are not well-formed. // diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index 5cc1ec71757be..f0fcbd5528f4e 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -8,15 +8,15 @@ //! specialization errors. These things can (and probably should) be //! fixed, but for the moment it's easier to do these checks early. -use crate::constrained_generic_params as cgp; +use crate::{constrained_generic_params as cgp, errors::UnconstrainedGenericParameter}; use min_specialization::check_min_specialization; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{codes::*, struct_span_code_err}; +use rustc_errors::codes::*; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::ErrorGuaranteed; mod min_specialization; @@ -117,43 +117,34 @@ fn enforce_impl_params_are_constrained( let mut res = Ok(()); for param in &impl_generics.own_params { - match param.kind { + let err = match param.kind { // Disallow ANY unconstrained type parameters. ty::GenericParamDefKind::Type { .. } => { let param_ty = ty::ParamTy::for_def(param); - if !input_parameters.contains(&cgp::Parameter::from(param_ty)) { - res = Err(report_unused_parameter( - tcx, - tcx.def_span(param.def_id), - "type", - param_ty.name, - )); - } + !input_parameters.contains(&cgp::Parameter::from(param_ty)) } ty::GenericParamDefKind::Lifetime => { let param_lt = cgp::Parameter::from(param.to_early_bound_region_data()); - if lifetimes_in_associated_types.contains(¶m_lt) && // (*) + lifetimes_in_associated_types.contains(¶m_lt) && // (*) !input_parameters.contains(¶m_lt) - { - res = Err(report_unused_parameter( - tcx, - tcx.def_span(param.def_id), - "lifetime", - param.name, - )); - } } ty::GenericParamDefKind::Const { .. } => { let param_ct = ty::ParamConst::for_def(param); - if !input_parameters.contains(&cgp::Parameter::from(param_ct)) { - res = Err(report_unused_parameter( - tcx, - tcx.def_span(param.def_id), - "const", - param_ct.name, - )); - } + !input_parameters.contains(&cgp::Parameter::from(param_ct)) } + }; + if err { + let const_param_note = + matches!(param.kind, ty::GenericParamDefKind::Const { .. }).then_some(()); + let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter { + span: tcx.def_span(param.def_id), + param_name: param.name, + param_def_kind: tcx.def_descr(param.def_id), + const_param_note, + const_param_note2: const_param_note, + }); + diag.code(E0207); + res = Err(diag.emit()); } } res @@ -177,30 +168,3 @@ fn enforce_impl_params_are_constrained( // associated types. I believe this is sound, because lifetimes // used elsewhere are not projected back out. } - -fn report_unused_parameter( - tcx: TyCtxt<'_>, - span: Span, - kind: &str, - name: Symbol, -) -> ErrorGuaranteed { - let mut err = struct_span_code_err!( - tcx.dcx(), - span, - E0207, - "the {} parameter `{}` is not constrained by the \ - impl trait, self type, or predicates", - kind, - name - ); - err.span_label(span, format!("unconstrained {kind} parameter")); - if kind == "const" { - err.note( - "expressions using a const parameter must map each value to a distinct output value", - ); - err.note( - "proving the result of expressions other than the parameter are unique is not supported", - ); - } - err.emit() -} diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 5105d60ae188c..db5eba0d9ebaf 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -12,8 +12,9 @@ use rustc_ast_pretty::pp::{self, Breaks}; use rustc_ast_pretty::pprust::{Comments, PrintState}; use rustc_hir as hir; use rustc_hir::{ - BindingMode, ByRef, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId, - LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, TraitBoundModifier, + BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind, + HirId, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, + TraitBoundModifier, }; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, Ident, Symbol}; @@ -87,6 +88,7 @@ impl<'a> State<'a> { Node::Variant(a) => self.print_variant(a), Node::AnonConst(a) => self.print_anon_const(a), Node::ConstBlock(a) => self.print_inline_const(a), + Node::ConstArg(a) => self.print_const_arg(a), Node::Expr(a) => self.print_expr(a), Node::ExprField(a) => self.print_expr_field(a), Node::Stmt(a) => self.print_stmt(a), @@ -983,7 +985,7 @@ impl<'a> State<'a> { fn print_array_length(&mut self, len: &hir::ArrayLen<'_>) { match len { hir::ArrayLen::Infer(..) => self.word("_"), - hir::ArrayLen::Body(ct) => self.print_anon_const(ct), + hir::ArrayLen::Body(ct) => self.print_const_arg(ct), } } @@ -991,6 +993,13 @@ impl<'a> State<'a> { self.ann.nested(self, Nested::Body(constant.body)) } + fn print_const_arg(&mut self, const_arg: &hir::ConstArg<'_>) { + match &const_arg.kind { + ConstArgKind::Path(qpath) => self.print_qpath(qpath, true), + ConstArgKind::Anon(anon) => self.print_anon_const(anon), + } + } + fn print_call_post(&mut self, args: &[hir::Expr<'_>]) { self.popen(); self.commasep_exprs(Inconsistent, args); @@ -1679,7 +1688,7 @@ impl<'a> State<'a> { GenericArg::Lifetime(lt) if !elide_lifetimes => s.print_lifetime(lt), GenericArg::Lifetime(_) => {} GenericArg::Type(ty) => s.print_type(ty), - GenericArg::Const(ct) => s.print_anon_const(&ct.value), + GenericArg::Const(ct) => s.print_const_arg(ct), GenericArg::Infer(_inf) => s.word("_"), } }); @@ -1720,7 +1729,7 @@ impl<'a> State<'a> { self.word_space("="); match term { Term::Ty(ty) => self.print_type(ty), - Term::Const(ref c) => self.print_anon_const(c), + Term::Const(ref c) => self.print_const_arg(c), } } hir::AssocItemConstraintKind::Bound { bounds } => { @@ -2155,7 +2164,7 @@ impl<'a> State<'a> { if let Some(default) = default { self.space(); self.word_space("="); - self.print_anon_const(default); + self.print_const_arg(default); } } } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 035a3429ed76e..d708269f1f538 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -519,7 +519,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_error(tcx, e) } Res::Def(DefKind::Variant, _) => { - let e = report_unexpected_variant_res(tcx, res, qpath, expr.span, E0533, "value"); + let e = report_unexpected_variant_res( + tcx, + res, + Some(expr), + qpath, + expr.span, + E0533, + "value", + ); Ty::new_error(tcx, e) } _ => { @@ -1439,9 +1447,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; }; if let hir::TyKind::Array(_, length) = ty.peel_refs().kind - && let hir::ArrayLen::Body(&hir::AnonConst { hir_id, .. }) = length + && let hir::ArrayLen::Body(ct) = length { - let span = self.tcx.hir().span(hir_id); + let span = ct.span(); self.dcx().try_steal_modify_and_emit_err( span, StashKey::UnderscoreForArrayLengths, @@ -2210,8 +2218,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let variant_ident_span = self.tcx.def_ident_span(variant.def_id).unwrap(); - match variant.ctor_kind() { - Some(CtorKind::Fn) => match ty.kind() { + match variant.ctor { + Some((CtorKind::Fn, def_id)) => match ty.kind() { ty::Adt(adt, ..) if adt.is_enum() => { err.span_label( variant_ident_span, @@ -2222,28 +2230,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); err.span_label(field.ident.span, "field does not exist"); + let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + let fields = format!( + "({})", + inputs.iter().map(|i| format!("/* {i} */")).collect::>().join(", ") + ); + let (replace_span, sugg) = match expr.kind { + hir::ExprKind::Struct(qpath, ..) => { + (qpath.span().shrink_to_hi().with_hi(expr.span.hi()), fields) + } + _ => { + (expr.span, format!("{ty}::{variant}{fields}", variant = variant.name)) + } + }; err.span_suggestion_verbose( - expr.span, + replace_span, format!( "`{adt}::{variant}` is a tuple {kind_name}, use the appropriate syntax", adt = ty, variant = variant.name, ), - format!( - "{adt}::{variant}(/* fields */)", - adt = ty, - variant = variant.name, - ), + sugg, Applicability::HasPlaceholders, ); } _ => { err.span_label(variant_ident_span, format!("`{ty}` defined here")); err.span_label(field.ident.span, "field does not exist"); + let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + let fields = format!( + "({})", + inputs.iter().map(|i| format!("/* {i} */")).collect::>().join(", ") + ); err.span_suggestion_verbose( expr.span, format!("`{ty}` is a tuple {kind_name}, use the appropriate syntax",), - format!("{ty}(/* fields */)"), + format!("{ty}{fields}"), Applicability::HasPlaceholders, ); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index ea9567f4e3dcb..cc2c1a302f58b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -19,7 +19,7 @@ use rustc_hir_analysis::hir_ty_lowering::{ GenericPathSegment, HirTyLowerer, IsMethodCall, RegionInferReason, }; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; -use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; +use rustc_infer::infer::need_type_info::TypeAnnotationNeeded; use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; @@ -457,22 +457,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn lower_array_length(&self, length: &hir::ArrayLen<'tcx>) -> ty::Const<'tcx> { match length { hir::ArrayLen::Infer(inf) => self.ct_infer(None, inf.span), - hir::ArrayLen::Body(anon_const) => { - let span = self.tcx.def_span(anon_const.def_id); - let c = ty::Const::from_anon_const(self.tcx, anon_const.def_id); + hir::ArrayLen::Body(const_arg) => { + let span = const_arg.span(); + let c = ty::Const::from_const_arg(self.tcx, const_arg, ty::FeedConstTy::No); self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None)); self.normalize(span, c) } } } - pub fn lower_const_arg(&self, hir_ct: &hir::AnonConst, param_def_id: DefId) -> ty::Const<'tcx> { - let did = hir_ct.def_id; - self.tcx.feed_anon_const_type(did, self.tcx.type_of(param_def_id)); - let ct = ty::Const::from_anon_const(self.tcx, did); + pub fn lower_const_arg( + &self, + const_arg: &'tcx hir::ConstArg<'tcx>, + param_def_id: DefId, + ) -> ty::Const<'tcx> { + let ct = + ty::Const::from_const_arg(self.tcx, const_arg, ty::FeedConstTy::Param(param_def_id)); self.register_wf_obligation( ct.into(), - self.tcx.hir().span(hir_ct.hir_id), + self.tcx.hir().span(const_arg.hir_id), ObligationCauseCode::WellFormed(None), ); ct @@ -1298,7 +1301,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.fcx.lower_ty(ty).raw.into() } (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.fcx.lower_const_arg(&ct.value, param.def_id).into() + self.fcx.lower_const_arg(ct, param.def_id).into() } (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { self.fcx.ty_infer(Some(param), inf.span).into() @@ -1519,7 +1522,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { let e = self.tainted_by_errors().unwrap_or_else(|| { self.err_ctxt() - .emit_inference_failure_err(self.body_id, sp, ty.into(), E0282, true) + .emit_inference_failure_err( + self.body_id, + sp, + ty.into(), + TypeAnnotationNeeded::E0282, + true, + ) .emit() }); let err = Ty::new_error(self.tcx, e); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 9fbb01216bbb6..2b4025ca8080d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -29,7 +29,7 @@ use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_index::IndexVec; -use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt}; +use rustc_infer::error_reporting::infer::{FailureCode, ObligationCauseExt}; use rustc_infer::infer::TypeTrace; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::ty::adjustment::AllowTwoPhase; @@ -1105,7 +1105,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { "".to_string() }; - labels.push((provided_span, format!("unexpected argument{provided_ty_name}"))); + let idx = if provided_arg_tys.len() == 1 { + "".to_string() + } else { + format!(" #{}", arg_idx.as_usize() + 1) + }; + labels.push(( + provided_span, + format!("unexpected argument{idx}{provided_ty_name}"), + )); let mut span = provided_span; if span.can_be_used_for_suggestions() && error_span.can_be_used_for_suggestions() @@ -1186,7 +1194,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { "".to_string() }; - labels.push((span, format!("an argument{rendered} is missing"))); + labels.push(( + span, + format!( + "argument #{}{rendered} is missing", + expected_idx.as_usize() + 1 + ), + )); + suggestion_text = match suggestion_text { SuggestionText::None => SuggestionText::Provide(false), SuggestionText::Provide(_) => SuggestionText::Provide(true), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index b5796fbd48a88..3fe87c03e7431 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -15,9 +15,9 @@ use hir::def_id::CRATE_DEF_ID; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, RegionInferReason}; +use rustc_infer::error_reporting::infer::sub_relations::SubRelations; +use rustc_infer::error_reporting::infer::TypeErrCtxt; use rustc_infer::infer; -use rustc_infer::infer::error_reporting::sub_relations::SubRelations; -use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; use rustc_span::symbol::Ident; diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index bdbdcee6446dc..2c79366450909 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -53,7 +53,7 @@ use crate::expectation::Expectation; use crate::fn_ctxt::LoweredTy; use crate::gather_locals::GatherLocalsVisitor; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed}; +use rustc_errors::{codes::*, struct_span_code_err, Applicability, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::Visitor; @@ -346,6 +346,7 @@ impl<'tcx> EnclosingBreakables<'tcx> { fn report_unexpected_variant_res( tcx: TyCtxt<'_>, res: Res, + expr: Option<&hir::Expr<'_>>, qpath: &hir::QPath<'_>, span: Span, err_code: ErrCode, @@ -356,7 +357,7 @@ fn report_unexpected_variant_res( _ => res.descr(), }; let path_str = rustc_hir_pretty::qpath_to_string(&tcx, qpath); - let err = tcx + let mut err = tcx .dcx() .struct_span_err(span, format!("expected {expected}, found {res_descr} `{path_str}`")) .with_code(err_code); @@ -366,6 +367,61 @@ fn report_unexpected_variant_res( err.with_span_label(span, "`fn` calls are not allowed in patterns") .with_help(format!("for more information, visit {patterns_url}")) } + Res::Def(DefKind::Variant, _) if let Some(expr) = expr => { + err.span_label(span, format!("not a {expected}")); + let variant = tcx.expect_variant_res(res); + let sugg = if variant.fields.is_empty() { + " {}".to_string() + } else { + format!( + " {{ {} }}", + variant + .fields + .iter() + .map(|f| format!("{}: /* value */", f.name)) + .collect::>() + .join(", ") + ) + }; + let descr = "you might have meant to create a new value of the struct"; + let mut suggestion = vec![]; + match tcx.parent_hir_node(expr.hir_id) { + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Call(..), + span: call_span, + .. + }) => { + suggestion.push((span.shrink_to_hi().with_hi(call_span.hi()), sugg)); + } + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(..), hir_id, .. }) => { + suggestion.push((expr.span.shrink_to_lo(), "(".to_string())); + if let hir::Node::Expr(drop_temps) = tcx.parent_hir_node(*hir_id) + && let hir::ExprKind::DropTemps(_) = drop_temps.kind + && let hir::Node::Expr(parent) = tcx.parent_hir_node(drop_temps.hir_id) + && let hir::ExprKind::If(condition, block, None) = parent.kind + && condition.hir_id == drop_temps.hir_id + && let hir::ExprKind::Block(block, _) = block.kind + && block.stmts.is_empty() + && let Some(expr) = block.expr + && let hir::ExprKind::Path(..) = expr.kind + { + // Special case: you can incorrectly write an equality condition: + // if foo == Struct { field } { /* if body */ } + // which should have been written + // if foo == (Struct { field }) { /* if body */ } + suggestion.push((block.span.shrink_to_hi(), ")".to_string())); + } else { + suggestion.push((span.shrink_to_hi().with_hi(expr.span.hi()), sugg)); + } + } + _ => { + suggestion.push((span.shrink_to_hi(), sugg)); + } + } + + err.multipart_suggestion_verbose(descr, suggestion, Applicability::MaybeIncorrect); + err + } _ => err.with_span_label(span, format!("not a {expected}")), } .emit() diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index e574fde14fb0a..e70431a68ff16 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -400,7 +400,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.cfcx.lower_ty(ty).raw.into() } (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.cfcx.lower_const_arg(&ct.value, param.def_id).into() + self.cfcx.lower_const_arg(ct, param.def_id).into() } (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { self.cfcx.ty_infer(Some(param), inf.span).into() diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index dc1b888374cf2..daf4ef5cdb324 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -182,8 +182,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_expr: &'tcx hir::Expr<'tcx>, args: &'tcx [hir::Expr<'tcx>], ) -> Result, MethodError<'tcx>> { - let pick = - self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; + let scope = if let Some(only_method) = segment.res.opt_def_id() { + ProbeScope::Single(only_method) + } else { + ProbeScope::TraitsInScope + }; + + let pick = self.lookup_probe(segment.ident, self_ty, call_expr, scope)?; self.lint_edition_dependent_dot_call( self_ty, segment, span, call_expr, self_expr, &pick, args, diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 6a7af5510e074..e817685e41c2b 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -12,7 +12,7 @@ use rustc_hir::HirId; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; -use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; +use rustc_infer::infer::need_type_info::TypeAnnotationNeeded; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_infer::traits::ObligationCauseCode; @@ -20,6 +20,7 @@ use rustc_middle::middle::stability; use rustc_middle::query::Providers; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::AssocItem; +use rustc_middle::ty::AssocItemContainer; use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::Upcast; use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; @@ -216,6 +217,9 @@ pub enum Mode { #[derive(PartialEq, Eq, Copy, Clone, Debug)] pub enum ProbeScope { + // Single candidate coming from pre-resolved delegation method. + Single(DefId), + // Assemble candidates coming only from traits in scope. TraitsInScope, @@ -441,7 +445,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.body_id, span, ty.into(), - E0282, + TypeAnnotationNeeded::E0282, !raw_ptr_call, ); if raw_ptr_call { @@ -480,12 +484,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_suggestion, ); - probe_cx.assemble_inherent_candidates(); match scope { ProbeScope::TraitsInScope => { - probe_cx.assemble_extension_candidates_for_traits_in_scope() + probe_cx.assemble_inherent_candidates(); + probe_cx.assemble_extension_candidates_for_traits_in_scope(); + } + ProbeScope::AllTraits => { + probe_cx.assemble_inherent_candidates(); + probe_cx.assemble_extension_candidates_for_all_traits(); + } + ProbeScope::Single(def_id) => { + let item = self.tcx.associated_item(def_id); + // FIXME(fn_delegation): Delegation to inherent methods is not yet supported. + assert_eq!(item.container, AssocItemContainer::TraitContainer); + + let trait_def_id = self.tcx.parent(def_id); + let trait_span = self.tcx.def_span(trait_def_id); + + let trait_args = self.fresh_args_for_item(trait_span, trait_def_id); + let trait_ref = ty::TraitRef::new_from_args(self.tcx, trait_def_id, trait_args); + + probe_cx.push_candidate( + Candidate { + item, + kind: CandidateKind::TraitCandidate(ty::Binder::dummy(trait_ref)), + import_ids: smallvec![], + }, + false, + ); } - ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits(), }; op(probe_cx) }) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 425289ce3c526..1cc7cf67ee31e 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1596,16 +1596,127 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // that had unsatisfied trait bounds if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() { let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT"); - if let Some(suggestion) = edit_distance::find_best_match_for_name( + if let Some(var_name) = edit_distance::find_best_match_for_name( &adt_def.variants().iter().map(|s| s.name).collect::>(), item_name.name, None, - ) { - err.span_suggestion( - span, + ) && let Some(variant) = adt_def.variants().iter().find(|s| s.name == var_name) + { + let mut suggestion = vec![(span, var_name.to_string())]; + if let SelfSource::QPath(ty) = source + && let hir::Node::Expr(ref path_expr) = self.tcx.parent_hir_node(ty.hir_id) + && let hir::ExprKind::Path(_) = path_expr.kind + && let hir::Node::Stmt(hir::Stmt { + kind: hir::StmtKind::Semi(ref parent), .. + }) + | hir::Node::Expr(ref parent) = self.tcx.parent_hir_node(path_expr.hir_id) + { + let replacement_span = + if let hir::ExprKind::Call(..) | hir::ExprKind::Struct(..) = parent.kind { + // We want to replace the parts that need to go, like `()` and `{}`. + span.with_hi(parent.span.hi()) + } else { + span + }; + match (variant.ctor, parent.kind) { + (None, hir::ExprKind::Struct(..)) => { + // We want a struct and we have a struct. We won't suggest changing + // the fields (at least for now). + suggestion = vec![(span, var_name.to_string())]; + } + (None, _) => { + // struct + suggestion = vec![( + replacement_span, + if variant.fields.is_empty() { + format!("{var_name} {{}}") + } else { + format!( + "{var_name} {{ {} }}", + variant + .fields + .iter() + .map(|f| format!("{}: /* value */", f.name)) + .collect::>() + .join(", ") + ) + }, + )]; + } + (Some((hir::def::CtorKind::Const, _)), _) => { + // unit, remove the `()`. + suggestion = vec![(replacement_span, var_name.to_string())]; + } + ( + Some((hir::def::CtorKind::Fn, def_id)), + hir::ExprKind::Call(rcvr, args), + ) => { + let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + // FIXME: reuse the logic for "change args" suggestion to account for types + // involved and detect things like substitution. + match (inputs, args) { + (inputs, []) => { + // Add arguments. + suggestion.push(( + rcvr.span.shrink_to_hi().with_hi(parent.span.hi()), + format!( + "({})", + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", ") + ), + )); + } + (_, [arg]) if inputs.len() != args.len() => { + // Replace arguments. + suggestion.push(( + arg.span, + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", "), + )); + } + (_, [arg_start, .., arg_end]) if inputs.len() != args.len() => { + // Replace arguments. + suggestion.push(( + arg_start.span.to(arg_end.span), + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", "), + )); + } + // Argument count is the same, keep as is. + _ => {} + } + } + (Some((hir::def::CtorKind::Fn, def_id)), _) => { + let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + suggestion = vec![( + replacement_span, + format!( + "{var_name}({})", + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", ") + ), + )]; + } + } + } + err.multipart_suggestion_verbose( "there is a variant with a similar name", suggestion, - Applicability::MaybeIncorrect, + Applicability::HasPlaceholders, ); } } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 6d1e9ff1f9527..8afc6a48dfc5c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1023,7 +1023,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => { let expected = "unit struct, unit variant or constant"; - let e = report_unexpected_variant_res(tcx, res, qpath, pat.span, E0533, expected); + let e = + report_unexpected_variant_res(tcx, res, None, qpath, pat.span, E0533, expected); return Ty::new_error(tcx, e); } Res::SelfCtor(def_id) => { @@ -1036,6 +1037,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let e = report_unexpected_variant_res( tcx, res, + None, qpath, pat.span, E0533, @@ -1189,7 +1191,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let report_unexpected_res = |res: Res| { let expected = "tuple struct or tuple variant"; - let e = report_unexpected_variant_res(tcx, res, qpath, pat.span, E0164, expected); + let e = report_unexpected_variant_res(tcx, res, None, qpath, pat.span, E0164, expected); on_error(e); e }; diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index e800c1a97d9eb..611854ce2afd0 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -8,7 +8,7 @@ use rustc_errors::{ErrorGuaranteed, StashKey}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirId; -use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; +use rustc_infer::infer::need_type_info::TypeAnnotationNeeded; use rustc_middle::span_bug; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; @@ -783,7 +783,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { self.fcx.tcx.hir().body_owner_def_id(self.body.id()), self.span.to_span(self.fcx.tcx), p.into(), - E0282, + TypeAnnotationNeeded::E0282, false, ) .emit() diff --git a/compiler/rustc_index/Cargo.toml b/compiler/rustc_index/Cargo.toml index 3a4c813b5d41b..92ea3f278dc43 100644 --- a/compiler/rustc_index/Cargo.toml +++ b/compiler/rustc_index/Cargo.toml @@ -15,5 +15,9 @@ smallvec = "1.8.1" [features] # tidy-alphabetical-start default = ["nightly"] -nightly = ["rustc_serialize", "rustc_macros", "rustc_index_macros/nightly"] +nightly = [ + "dep:rustc_serialize", + "dep:rustc_macros", + "rustc_index_macros/nightly", +] # tidy-alphabetical-end diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs index b866c8b8433d9..346ce945bf94b 100644 --- a/compiler/rustc_index/src/vec.rs +++ b/compiler/rustc_index/src/vec.rs @@ -1,4 +1,4 @@ -#[cfg(feature = "rustc_serialize")] +#[cfg(feature = "nightly")] use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use std::borrow::{Borrow, BorrowMut}; @@ -322,14 +322,14 @@ impl From<[T; N]> for IndexVec { } } -#[cfg(feature = "rustc_serialize")] +#[cfg(feature = "nightly")] impl> Encodable for IndexVec { fn encode(&self, s: &mut S) { Encodable::encode(&self.raw, s); } } -#[cfg(feature = "rustc_serialize")] +#[cfg(feature = "nightly")] impl> Decodable for IndexVec { fn decode(d: &mut D) -> Self { IndexVec::from_raw(Vec::::decode(d)) diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index 7a5e71599203e..c279195a7e99c 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -225,6 +225,8 @@ infer_outlives_content = lifetime of reference outlives lifetime of borrowed con infer_precise_capturing_existing = add `{$new_lifetime}` to the `use<...>` bound to explicitly capture it infer_precise_capturing_new = add a `use<...>` bound to explicitly capture `{$new_lifetime}` +infer_precise_capturing_new_but_apit = add a `use<...>` bound to explicitly capture `{$new_lifetime}` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate + infer_prlf_defined_with_sub = the lifetime `{$sub_symbol}` defined here... infer_prlf_defined_without_sub = the lifetime defined here... infer_prlf_known_limitation = this is a known limitation that will be removed in the future (see issue #100013 for more information) @@ -387,6 +389,9 @@ infer_type_annotations_needed = {$source_kind -> .label = type must be known at this point infer_types_declared_different = these two types are declared with different lifetimes... + +infer_warn_removing_apit_params = you could use a `use<...>` bound to explicitly capture `{$new_lifetime}`, but argument-position `impl Trait`s are not nameable + infer_where_copy_predicates = copy the `where` clause predicates from the trait infer_where_remove = remove the `where` clause diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/error_reporting/infer/mod.rs similarity index 99% rename from compiler/rustc_infer/src/infer/error_reporting/mod.rs rename to compiler/rustc_infer/src/error_reporting/infer/mod.rs index bb1285ee8135d..9998fbca056c3 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/error_reporting/infer/mod.rs @@ -45,17 +45,11 @@ //! ported to this system, and which relies on string concatenation at the //! time of error detection. -use super::{InferCtxt, TypeTrace, ValuePairs}; - -use crate::errors::{ObligationCauseFailureCode, TypeErrorAdditionalDiags}; -use crate::infer; -use crate::infer::ExpectedFound; -use crate::traits::{ - IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, - PredicateObligation, -}; +use std::borrow::Cow; +use std::ops::{ControlFlow, Deref}; +use std::path::PathBuf; +use std::{cmp, fmt, iter}; -use crate::infer::relate::{self, RelateResult, TypeRelation}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{ pluralize, Applicability, Diag, DiagCtxtHandle, DiagStyledString, IntoDiagArg, StringPart, @@ -68,6 +62,7 @@ use rustc_hir::{self as hir}; use rustc_macros::extension; use rustc_middle::bug; use rustc_middle::dep_graph::DepContext; +use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError, PrintTraitRefExt as _}; use rustc_middle::ty::{ @@ -76,18 +71,21 @@ use rustc_middle::ty::{ }; use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; -use std::borrow::Cow; -use std::ops::{ControlFlow, Deref}; -use std::path::PathBuf; -use std::{cmp, fmt, iter}; + +use crate::errors::{ObligationCauseFailureCode, TypeErrorAdditionalDiags}; +use crate::infer; +use crate::infer::relate::{self, RelateResult, TypeRelation}; +use crate::infer::{InferCtxt, TypeTrace, ValuePairs}; +use crate::traits::{ + IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, + PredicateObligation, +}; mod note_and_explain; mod suggest; -pub(crate) mod need_type_info; -pub mod sub_relations; -pub use need_type_info::TypeAnnotationNeeded; pub mod region; +pub mod sub_relations; pub mod nice_region_error; @@ -1242,7 +1240,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Some(values) => { let values = self.resolve_vars_if_possible(values); let (is_simple_error, exp_found) = match values { - ValuePairs::Terms(infer::ExpectedFound { expected, found }) => { + ValuePairs::Terms(ExpectedFound { expected, found }) => { match (expected.unpack(), found.unpack()) { (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => { let is_simple_err = expected.is_simple_text(self.tcx) @@ -1254,7 +1252,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ( is_simple_err, - Mismatch::Variable(infer::ExpectedFound { expected, found }), + Mismatch::Variable(ExpectedFound { expected, found }), ) } (ty::TermKind::Const(_), ty::TermKind::Const(_)) => { @@ -1263,13 +1261,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => (false, Mismatch::Fixed("type")), } } - ValuePairs::PolySigs(infer::ExpectedFound { expected, found }) => { + ValuePairs::PolySigs(ExpectedFound { expected, found }) => { OpaqueTypesVisitor::visit_expected_found(self.tcx, expected, found, span) .report(diag); (false, Mismatch::Fixed("signature")) } ValuePairs::TraitRefs(_) => (false, Mismatch::Fixed("trait")), - ValuePairs::Aliases(infer::ExpectedFound { expected, .. }) => { + ValuePairs::Aliases(ExpectedFound { expected, .. }) => { (false, Mismatch::Fixed(self.tcx.def_descr(expected.def_id))) } ValuePairs::Regions(_) => (false, Mismatch::Fixed("lifetime")), @@ -1303,9 +1301,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; if let Some((sp, msg)) = secondary_span { if swap_secondary_and_primary { - let terr = if let Some(infer::ValuePairs::Terms(infer::ExpectedFound { - expected, - .. + let terr = if let Some(infer::ValuePairs::Terms(ExpectedFound { + expected, .. })) = values { Cow::from(format!("expected this to be `{expected}`")) @@ -1765,9 +1762,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; if let Some(tykind) = tykind && let hir::TyKind::Array(_, length) = tykind - && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length + && let hir::ArrayLen::Body(ct) = length { - let span = self.tcx.hir().span(*hir_id); + let span = ct.span(); Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { span, length: sz.found }) } else { None @@ -1933,14 +1930,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { struct SameTypeModuloInfer<'a, 'tcx>(&'a InferCtxt<'tcx>); impl<'tcx> TypeRelation> for SameTypeModuloInfer<'_, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { self.0.tcx } - fn tag(&self) -> &'static str { - "SameTypeModuloInfer" - } - fn relate_with_variance>>( &mut self, _variance: ty::Variance, diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/different_lifetimes.rs similarity index 94% rename from compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs rename to compiler/rustc_infer/src/error_reporting/infer/nice_region_error/different_lifetimes.rs index cbeec591960b3..74dcde03639d6 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/different_lifetimes.rs @@ -1,21 +1,20 @@ //! Error Reporting for Anonymous Region Lifetime Errors //! where both the regions are anonymous. +use crate::error_reporting::infer::nice_region_error::find_anon_type::find_anon_type; +use crate::error_reporting::infer::nice_region_error::util::AnonymousParamInfo; +use crate::error_reporting::infer::nice_region_error::NiceRegionError; use crate::errors::AddLifetimeParamsSuggestion; use crate::errors::LifetimeMismatch; use crate::errors::LifetimeMismatchLabels; -use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; -use crate::infer::error_reporting::nice_region_error::util::AnonymousParamInfo; -use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::RegionResolutionError; use crate::infer::SubregionOrigin; -use crate::infer::TyCtxt; use rustc_errors::Subdiagnostic; use rustc_errors::{Diag, ErrorGuaranteed}; use rustc_hir::def_id::LocalDefId; use rustc_hir::Ty; -use rustc_middle::ty::Region; +use rustc_middle::ty::{Region, TyCtxt}; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when both the concerned regions are anonymous. diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/find_anon_type.rs similarity index 100% rename from compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs rename to compiler/rustc_infer/src/error_reporting/infer/nice_region_error/find_anon_type.rs diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs similarity index 97% rename from compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs rename to compiler/rustc_infer/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs index 7996b4bf65b4f..550cc455e0181 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs @@ -1,12 +1,12 @@ //! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate //! to hold. +use crate::error_reporting::infer::nice_region_error::NiceRegionError; use crate::errors::{note_and_explain, IntroducesStaticBecauseUnmetLifetimeReq}; use crate::errors::{ DoesNotOutliveStaticFromImpl, ImplicitStaticLifetimeSubdiag, MismatchedStaticLifetime, }; -use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::ObligationCauseCode; use rustc_data_structures::fx::FxIndexSet; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/mod.rs similarity index 94% rename from compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs rename to compiler/rustc_infer/src/error_reporting/infer/nice_region_error/mod.rs index cffdfa887523d..ced4c384f0261 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs +++ b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/mod.rs @@ -1,6 +1,6 @@ -use crate::infer::error_reporting::TypeErrCtxt; -use crate::infer::lexical_region_resolve::RegionResolutionError; -use crate::infer::lexical_region_resolve::RegionResolutionError::*; +use crate::error_reporting::infer::TypeErrCtxt; +use crate::infer::RegionResolutionError; +use crate::infer::RegionResolutionError::*; use rustc_errors::{Diag, ErrorGuaranteed}; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::{self, TyCtxt}; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs similarity index 94% rename from compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs rename to compiler/rustc_infer/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs index 85624c9ca73db..d1802d2f5eeb0 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs @@ -1,11 +1,9 @@ //! Error Reporting for Anonymous Region Lifetime Errors //! where one region is named and the other is anonymous. -use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use crate::{ - errors::ExplicitLifetimeRequired, - infer::error_reporting::nice_region_error::find_anon_type::find_anon_type, -}; +use crate::error_reporting::infer::nice_region_error::find_anon_type::find_anon_type; +use crate::error_reporting::infer::nice_region_error::NiceRegionError; +use crate::errors::ExplicitLifetimeRequired; use rustc_errors::Diag; use rustc_middle::ty; use rustc_span::symbol::kw; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_error.rs similarity index 99% rename from compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs rename to compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_error.rs index 80b7e3b4fa509..476ac3f1720b8 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_error.rs @@ -1,9 +1,9 @@ +use crate::error_reporting::infer::nice_region_error::NiceRegionError; use crate::errors::{ ActualImplExpectedKind, ActualImplExpectedLifetimeKind, ActualImplExplNotes, TraitPlaceholderMismatch, TyOrSig, }; -use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::RegionResolutionError; use crate::infer::ValuePairs; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::{ObligationCause, ObligationCauseCode}; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_relation.rs similarity index 94% rename from compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs rename to compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_relation.rs index a3f306802de06..e9f17a3e3e2d0 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs +++ b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_relation.rs @@ -1,9 +1,6 @@ -use crate::{ - errors::PlaceholderRelationLfNotSatisfied, - infer::{ - error_reporting::nice_region_error::NiceRegionError, RegionResolutionError, SubregionOrigin, - }, -}; +use crate::error_reporting::infer::nice_region_error::NiceRegionError; +use crate::errors::PlaceholderRelationLfNotSatisfied; +use crate::infer::{RegionResolutionError, SubregionOrigin}; use rustc_data_structures::intern::Interned; use rustc_errors::Diag; use rustc_middle::ty::{self, RePlaceholder, Region}; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/static_impl_trait.rs similarity index 99% rename from compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs rename to compiler/rustc_infer/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 9973646aecd6a..ce157ff3dc8d9 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -1,11 +1,11 @@ //! Error Reporting for static impl Traits. +use crate::error_reporting::infer::nice_region_error::NiceRegionError; use crate::errors::{ ButCallingIntroduces, ButNeedsToSatisfy, DynTraitConstraintSuggestion, MoreTargeted, ReqIntroducedLocations, }; -use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_data_structures::fx::FxIndexSet; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs similarity index 97% rename from compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs rename to compiler/rustc_infer/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs index 7f3e23716f984..c58c7e1355174 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs @@ -1,8 +1,8 @@ //! Error Reporting for `impl` items that do not match the obligations from their `trait`. +use crate::error_reporting::infer::nice_region_error::NiceRegionError; use crate::errors::{ConsiderBorrowingParamHelp, RelationshipHelp, TraitImplDiff}; -use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::RegionResolutionError; use crate::infer::{Subtype, ValuePairs}; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/util.rs similarity index 97% rename from compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs rename to compiler/rustc_infer/src/error_reporting/infer/nice_region_error/util.rs index 83145e4f7b27c..30fa98c55265e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/util.rs @@ -1,13 +1,13 @@ //! Helper functions corresponding to lifetime errors due to //! anonymous regions. -use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use crate::infer::TyCtxt; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_middle::ty::{self, Binder, Region, Ty, TypeFoldable}; +use rustc_middle::ty::{self, Binder, Region, Ty, TyCtxt, TypeFoldable}; use rustc_span::Span; +use crate::error_reporting::infer::nice_region_error::NiceRegionError; + /// Information about the anonymous region we are searching for. #[derive(Debug)] pub struct AnonymousParamInfo<'tcx> { diff --git a/compiler/rustc_infer/src/error_reporting/infer/note.rs b/compiler/rustc_infer/src/error_reporting/infer/note.rs new file mode 100644 index 0000000000000..aeb3049c2ae96 --- /dev/null +++ b/compiler/rustc_infer/src/error_reporting/infer/note.rs @@ -0,0 +1,421 @@ +use crate::error_reporting::infer::{note_and_explain_region, TypeErrCtxt}; +use crate::errors::{ + note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent, + RefLongerThanData, RegionOriginNote, WhereClauseSuggestions, +}; +use crate::fluent_generated as fluent; +use crate::infer::{self, SubregionOrigin}; +use rustc_errors::{Diag, Subdiagnostic}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::traits::ObligationCauseCode; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::{self, IsSuggestable, Region, Ty}; +use rustc_span::symbol::kw; + +use super::ObligationCauseAsDiagArg; + +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) { + match *origin { + infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { + span: trace.cause.span, + requirement: ObligationCauseAsDiagArg(trace.cause.clone()), + expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)), + } + .add_to_diag(err), + infer::Reborrow(span) => { + RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err) + } + infer::RelateObjectBound(span) => { + RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound } + .add_to_diag(err); + } + infer::ReferenceOutlivesReferent(ty, span) => { + RegionOriginNote::WithName { + span, + msg: fluent::infer_reference_outlives_referent, + name: &self.ty_to_string(ty), + continues: false, + } + .add_to_diag(err); + } + infer::RelateParamBound(span, ty, opt_span) => { + RegionOriginNote::WithName { + span, + msg: fluent::infer_relate_param_bound, + name: &self.ty_to_string(ty), + continues: opt_span.is_some(), + } + .add_to_diag(err); + if let Some(span) = opt_span { + RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 } + .add_to_diag(err); + } + } + infer::RelateRegionParamBound(span) => { + RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound } + .add_to_diag(err); + } + infer::CompareImplItemObligation { span, .. } => { + RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation } + .add_to_diag(err); + } + infer::CheckAssociatedTypeBounds { ref parent, .. } => { + self.note_region_origin(err, parent); + } + infer::AscribeUserTypeProvePredicate(span) => { + RegionOriginNote::Plain { + span, + msg: fluent::infer_ascribe_user_type_prove_predicate, + } + .add_to_diag(err); + } + } + } + + pub(super) fn report_concrete_failure( + &self, + generic_param_scope: LocalDefId, + origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) -> Diag<'a> { + let mut err = match origin { + infer::Subtype(box trace) => { + let terr = TypeError::RegionsDoesNotOutlive(sup, sub); + let mut err = self.report_and_explain_type_error(trace, terr); + match (*sub, *sup) { + (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} + (ty::RePlaceholder(_), _) => { + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "", + sup, + " doesn't meet the lifetime requirements", + None, + ); + } + (_, ty::RePlaceholder(_)) => { + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "the required lifetime does not necessarily outlive ", + sub, + "", + None, + ); + } + _ => { + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "", + sup, + "...", + None, + ); + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "...does not necessarily outlive ", + sub, + "", + None, + ); + } + } + err + } + infer::Reborrow(span) => { + let reference_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::RefValidFor, + note_and_explain::SuffixKind::Continues, + ); + let content_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::ContentValidFor, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(OutlivesContent { + span, + notes: reference_valid.into_iter().chain(content_valid).collect(), + }) + } + infer::RelateObjectBound(span) => { + let object_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::TypeObjValidFor, + note_and_explain::SuffixKind::Empty, + ); + let pointer_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::SourcePointerValidFor, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(OutlivesBound { + span, + notes: object_valid.into_iter().chain(pointer_valid).collect(), + }) + } + infer::RelateParamBound(span, ty, opt_span) => { + let prefix = match *sub { + ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy, + _ => note_and_explain::PrefixKind::TypeOutlive, + }; + let suffix = if opt_span.is_some() { + note_and_explain::SuffixKind::ReqByBinding + } else { + note_and_explain::SuffixKind::Empty + }; + let note = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + opt_span, + prefix, + suffix, + ); + self.dcx().create_err(FulfillReqLifetime { + span, + ty: self.resolve_vars_if_possible(ty), + note, + }) + } + infer::RelateRegionParamBound(span) => { + let param_instantiated = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::LfParamInstantiatedWith, + note_and_explain::SuffixKind::Empty, + ); + let param_must_outlive = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::LfParamMustOutlive, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(LfBoundNotSatisfied { + span, + notes: param_instantiated.into_iter().chain(param_must_outlive).collect(), + }) + } + infer::ReferenceOutlivesReferent(ty, span) => { + let pointer_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::PointerValidFor, + note_and_explain::SuffixKind::Empty, + ); + let data_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::DataValidFor, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(RefLongerThanData { + span, + ty: self.resolve_vars_if_possible(ty), + notes: pointer_valid.into_iter().chain(data_valid).collect(), + }) + } + infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => { + let mut err = self.infcx.report_extra_impl_obligation( + span, + impl_item_def_id, + trait_item_def_id, + &format!("`{sup}: {sub}`"), + ); + // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause + if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) + && generics.where_clause_span.contains(span) + { + self.suggest_copy_trait_method_bounds( + trait_item_def_id, + impl_item_def_id, + &mut err, + ); + } + err + } + infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => { + let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup); + + // Don't mention the item name if it's an RPITIT, since that'll just confuse + // folks. + if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) { + let trait_item_span = self.tcx.def_span(trait_item_def_id); + let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); + err.span_label( + trait_item_span, + format!("definition of `{item_name}` from trait"), + ); + } + + self.suggest_copy_trait_method_bounds( + trait_item_def_id, + impl_item_def_id, + &mut err, + ); + err + } + infer::AscribeUserTypeProvePredicate(span) => { + let instantiated = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::LfInstantiatedWith, + note_and_explain::SuffixKind::Empty, + ); + let must_outlive = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::LfMustOutlive, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(LfBoundNotSatisfied { + span, + notes: instantiated.into_iter().chain(must_outlive).collect(), + }) + } + }; + if sub.is_error() || sup.is_error() { + err.downgrade_to_delayed_bug(); + } + err + } + + pub fn suggest_copy_trait_method_bounds( + &self, + trait_item_def_id: DefId, + impl_item_def_id: LocalDefId, + err: &mut Diag<'_>, + ) { + // FIXME(compiler-errors): Right now this is only being used for region + // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches, + // but right now it's not really very smart when it comes to implicit `Sized` + // predicates and bounds on the trait itself. + + let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx) + else { + return; + }; + let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else { + return; + }; + let trait_args = trait_ref + .instantiate_identity() + // Replace the explicit self type with `Self` for better suggestion rendering + .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper)) + .args; + let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id) + .rebase_onto(self.tcx, impl_def_id, trait_args); + + let Ok(trait_predicates) = + self.tcx + .explicit_predicates_of(trait_item_def_id) + .instantiate_own(self.tcx, trait_item_args) + .map(|(pred, _)| { + if pred.is_suggestable(self.tcx, false) { + Ok(pred.to_string()) + } else { + Err(()) + } + }) + .collect::, ()>>() + else { + return; + }; + + let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else { + return; + }; + + let suggestion = if trait_predicates.is_empty() { + WhereClauseSuggestions::Remove { span: generics.where_clause_span } + } else { + let space = if generics.where_clause_span.is_empty() { " " } else { "" }; + WhereClauseSuggestions::CopyPredicates { + span: generics.where_clause_span, + space, + trait_predicates: trait_predicates.join(", "), + } + }; + err.subdiagnostic(suggestion); + } + + pub(super) fn report_placeholder_failure( + &self, + generic_param_scope: LocalDefId, + placeholder_origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) -> Diag<'a> { + // I can't think how to do better than this right now. -nikomatsakis + debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure"); + match placeholder_origin { + infer::Subtype(box ref trace) + if matches!( + &trace.cause.code().peel_derives(), + ObligationCauseCode::WhereClause(..) + | ObligationCauseCode::WhereClauseInExpr(..) + ) => + { + // Hack to get around the borrow checker because trace.cause has an `Rc`. + if let ObligationCauseCode::WhereClause(_, span) + | ObligationCauseCode::WhereClauseInExpr(_, span, ..) = + &trace.cause.code().peel_derives() + && !span.is_dummy() + { + let span = *span; + self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup) + .with_span_note(span, "the lifetime requirement is introduced here") + } else { + unreachable!( + "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..." + ) + } + } + infer::Subtype(box trace) => { + let terr = TypeError::RegionsPlaceholderMismatch; + return self.report_and_explain_type_error(trace, terr); + } + _ => { + return self.report_concrete_failure( + generic_param_scope, + placeholder_origin, + sub, + sup, + ); + } + } + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/error_reporting/infer/note_and_explain.rs similarity index 100% rename from compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs rename to compiler/rustc_infer/src/error_reporting/infer/note_and_explain.rs diff --git a/compiler/rustc_infer/src/infer/error_reporting/region.rs b/compiler/rustc_infer/src/error_reporting/infer/region.rs similarity index 94% rename from compiler/rustc_infer/src/infer/error_reporting/region.rs rename to compiler/rustc_infer/src/error_reporting/infer/region.rs index 191cb23184da1..5d41bb5d2710c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/region.rs +++ b/compiler/rustc_infer/src/error_reporting/infer/region.rs @@ -18,12 +18,12 @@ use rustc_type_ir::Upcast as _; use super::nice_region_error::find_anon_type; use super::{nice_region_error, ObligationCauseAsDiagArg}; +use crate::error_reporting::infer::{ObligationCauseExt as _, TypeErrCtxt}; use crate::errors::{ self, note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent, RefLongerThanData, RegionOriginNote, WhereClauseSuggestions, }; use crate::fluent_generated as fluent; -use crate::infer::error_reporting::{ObligationCauseExt as _, TypeErrCtxt}; use crate::infer::region_constraints::GenericKind; use crate::infer::{self, InferCtxt, RegionResolutionError, RegionVariableOrigin, SubregionOrigin}; @@ -1269,9 +1269,13 @@ fn suggest_precise_capturing<'tcx>( captured_lifetime: ty::Region<'tcx>, diag: &mut Diag<'_>, ) { - let hir::OpaqueTy { bounds, .. } = + let hir::OpaqueTy { bounds, origin, .. } = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); + let hir::OpaqueTyOrigin::FnReturn(fn_def_id) = *origin else { + return; + }; + let new_lifetime = Symbol::intern(&captured_lifetime.to_string()); if let Some((args, span)) = bounds.iter().find_map(|bound| match bound { @@ -1306,6 +1310,7 @@ fn suggest_precise_capturing<'tcx>( let variances = tcx.variances_of(opaque_def_id); let mut generics = tcx.generics_of(opaque_def_id); + let mut synthetics = vec![]; loop { for param in &generics.own_params { if variances[param.index as usize] == ty::Bivariant { @@ -1317,9 +1322,7 @@ fn suggest_precise_capturing<'tcx>( captured_lifetimes.insert(param.name); } ty::GenericParamDefKind::Type { synthetic: true, .. } => { - // FIXME: We can't provide a good suggestion for - // `use<...>` if we have an APIT. Bail for now. - return; + synthetics.push((tcx.def_span(param.def_id), param.name)); } ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => { @@ -1340,17 +1343,86 @@ fn suggest_precise_capturing<'tcx>( return; } - let concatenated_bounds = captured_lifetimes - .into_iter() - .chain(captured_non_lifetimes) - .map(|sym| sym.to_string()) - .collect::>() - .join(", "); - - diag.subdiagnostic(errors::AddPreciseCapturing::New { - span: tcx.def_span(opaque_def_id).shrink_to_hi(), - new_lifetime, - concatenated_bounds, - }); + if synthetics.is_empty() { + let concatenated_bounds = captured_lifetimes + .into_iter() + .chain(captured_non_lifetimes) + .map(|sym| sym.to_string()) + .collect::>() + .join(", "); + + diag.subdiagnostic(errors::AddPreciseCapturing::New { + span: tcx.def_span(opaque_def_id).shrink_to_hi(), + new_lifetime, + concatenated_bounds, + }); + } else { + let mut next_fresh_param = || { + ["T", "U", "V", "W", "X", "Y", "A", "B", "C"] + .into_iter() + .map(Symbol::intern) + .chain((0..).map(|i| Symbol::intern(&format!("T{i}")))) + .find(|s| captured_non_lifetimes.insert(*s)) + .unwrap() + }; + + let mut new_params = String::new(); + let mut suggs = vec![]; + let mut apit_spans = vec![]; + + for (i, (span, name)) in synthetics.into_iter().enumerate() { + apit_spans.push(span); + + let fresh_param = next_fresh_param(); + + // Suggest renaming. + suggs.push((span, fresh_param.to_string())); + + // Super jank. Turn `impl Trait` into `T: Trait`. + // + // This currently involves stripping the `impl` from the name of + // the parameter, since APITs are always named after how they are + // rendered in the AST. This sucks! But to recreate the bound list + // from the APIT itself would be miserable, so we're stuck with + // this for now! + if i > 0 { + new_params += ", "; + } + let name_as_bounds = name.as_str().trim_start_matches("impl").trim_start(); + new_params += fresh_param.as_str(); + new_params += ": "; + new_params += name_as_bounds; + } + + let Some(generics) = tcx.hir().get_generics(fn_def_id) else { + // This shouldn't happen, but don't ICE. + return; + }; + + // Add generics or concatenate to the end of the list. + suggs.push(if let Some(params_span) = generics.span_for_param_suggestion() { + (params_span, format!(", {new_params}")) + } else { + (generics.span, format!("<{new_params}>")) + }); + + let concatenated_bounds = captured_lifetimes + .into_iter() + .chain(captured_non_lifetimes) + .map(|sym| sym.to_string()) + .collect::>() + .join(", "); + + suggs.push(( + tcx.def_span(opaque_def_id).shrink_to_hi(), + format!(" + use<{concatenated_bounds}>"), + )); + + diag.subdiagnostic(errors::AddPreciseCapturingAndParams { + suggs, + new_lifetime, + apit_spans, + }); + } } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/sub_relations.rs b/compiler/rustc_infer/src/error_reporting/infer/sub_relations.rs similarity index 100% rename from compiler/rustc_infer/src/infer/error_reporting/sub_relations.rs rename to compiler/rustc_infer/src/error_reporting/infer/sub_relations.rs diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/error_reporting/infer/suggest.rs similarity index 99% rename from compiler/rustc_infer/src/infer/error_reporting/suggest.rs rename to compiler/rustc_infer/src/error_reporting/infer/suggest.rs index 13b145296a7c9..4d11ab9fac68e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/error_reporting/infer/suggest.rs @@ -1,4 +1,4 @@ -use crate::infer::error_reporting::hir::Path; +use crate::error_reporting::infer::hir::Path; use core::ops::ControlFlow; use hir::def::CtorKind; use hir::intravisit::{walk_expr, walk_stmt, Visitor}; diff --git a/compiler/rustc_infer/src/error_reporting/mod.rs b/compiler/rustc_infer/src/error_reporting/mod.rs new file mode 100644 index 0000000000000..132485ec6615f --- /dev/null +++ b/compiler/rustc_infer/src/error_reporting/mod.rs @@ -0,0 +1 @@ +pub mod infer; diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index ce1b0f86d0341..2ce712e0bff58 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -15,11 +15,10 @@ use rustc_span::symbol::kw; use rustc_span::Symbol; use rustc_span::{symbol::Ident, BytePos, Span}; +use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlighted; +use crate::error_reporting::infer::ObligationCauseAsDiagArg; use crate::fluent_generated as fluent; -use crate::infer::error_reporting::{ - need_type_info::UnderspecifiedArgKind, nice_region_error::placeholder_error::Highlighted, - ObligationCauseAsDiagArg, -}; +use crate::infer::need_type_info::UnderspecifiedArgKind; use std::path::PathBuf; @@ -1610,3 +1609,25 @@ pub enum AddPreciseCapturing { post: &'static str, }, } + +pub struct AddPreciseCapturingAndParams { + pub suggs: Vec<(Span, String)>, + pub new_lifetime: Symbol, + pub apit_spans: Vec, +} + +impl Subdiagnostic for AddPreciseCapturingAndParams { + fn add_to_diag_with>( + self, + diag: &mut Diag<'_, G>, + _f: &F, + ) { + diag.arg("new_lifetime", self.new_lifetime); + diag.multipart_suggestion_verbose( + fluent::infer_precise_capturing_new_but_apit, + self.suggs, + Applicability::MaybeIncorrect, + ); + diag.span_note(self.apit_spans, fluent::infer_warn_removing_apit_params); + } +} diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index 4fbeb0ec10246..d71b7f3c264e8 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -1,5 +1,5 @@ +use crate::error_reporting::infer::nice_region_error::find_anon_type; use crate::fluent_generated as fluent; -use crate::infer::error_reporting::nice_region_error::find_anon_type; use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, SubdiagMessageOp, Subdiagnostic}; use rustc_hir::def_id::LocalDefId; use rustc_middle::bug; diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index a0be545d46fed..c9073d8c23e5a 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -11,9 +11,9 @@ pub use BoundRegionConversionTime::*; pub use RegionVariableOrigin::*; pub use SubregionOrigin::*; +use crate::error_reporting::infer::TypeErrCtxt; use crate::infer::relate::RelateResult; use crate::traits::{self, ObligationCause, ObligationInspector, PredicateObligation, TraitEngine}; -use error_reporting::TypeErrCtxt; use free_regions::RegionRelations; use lexical_region_resolve::LexicalRegionResolutions; use opaque_types::OpaqueTypeStorage; @@ -54,7 +54,6 @@ use type_variable::TypeVariableOrigin; pub mod at; pub mod canonical; mod context; -pub mod error_reporting; pub mod free_regions; mod freshen; mod lexical_region_resolve; @@ -66,6 +65,8 @@ pub mod relate; pub mod resolve; pub(crate) mod snapshot; pub mod type_variable; +// FIXME(error_reporting): Where should we put this? +pub mod need_type_info; #[must_use] #[derive(Debug)] @@ -1426,17 +1427,17 @@ impl<'tcx> InferCtxt<'tcx> { span: Span, ) -> Result, ErrorHandled> { match self.const_eval_resolve(param_env, unevaluated, span) { - Ok(Some(val)) => Ok(ty::Const::new_value( + Ok(Ok(val)) => Ok(ty::Const::new_value( self.tcx, val, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args), )), - Ok(None) => { + Ok(Err(bad_ty)) => { let tcx = self.tcx; let def_id = unevaluated.def; span_bug!( tcx.def_span(def_id), - "unable to construct a constant value for the unevaluated constant {:?}", + "unable to construct a valtree for the unevaluated constant {:?}: type {bad_ty} is not valtree-compatible", unevaluated ); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/need_type_info.rs similarity index 99% rename from compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs rename to compiler/rustc_infer/src/infer/need_type_info.rs index 084aebc296f86..4f3dcd9043fd0 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/need_type_info.rs @@ -1,8 +1,8 @@ +use crate::error_reporting::infer::TypeErrCtxt; use crate::errors::{ AmbiguousImpl, AmbiguousReturn, AnnotationRequired, InferenceBadError, SourceKindMultiSuggestion, SourceKindSubdiag, }; -use crate::infer::error_reporting::TypeErrCtxt; use crate::infer::InferCtxt; use rustc_errors::{codes::*, Diag, IntoDiagArg}; use rustc_hir as hir; diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index 978b92fd8980d..c63eeaf812c4e 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -110,7 +110,7 @@ impl<'tcx> MatchAgainstHigherRankedOutlives<'tcx> { /// Binds the pattern variable `br` to `value`; returns an `Err` if the pattern /// is already bound to a different value. - #[instrument(level = "debug", skip(self))] + #[instrument(level = "trace", skip(self))] fn bind( &mut self, br: ty::BoundRegion, @@ -133,11 +133,7 @@ impl<'tcx> MatchAgainstHigherRankedOutlives<'tcx> { } impl<'tcx> TypeRelation> for MatchAgainstHigherRankedOutlives<'tcx> { - fn tag(&self) -> &'static str { - "MatchAgainstHigherRankedOutlives" - } - - fn tcx(&self) -> TyCtxt<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -154,13 +150,12 @@ impl<'tcx> TypeRelation> for MatchAgainstHigherRankedOutlives<'tcx> if variance != ty::Bivariant { self.relate(a, b) } else { Ok(a) } } - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "trace")] fn regions( &mut self, pattern: ty::Region<'tcx>, value: ty::Region<'tcx>, ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!("self.pattern_depth = {:?}", self.pattern_depth); if let ty::RegionKind::ReBound(depth, br) = pattern.kind() && depth == self.pattern_depth { @@ -172,7 +167,7 @@ impl<'tcx> TypeRelation> for MatchAgainstHigherRankedOutlives<'tcx> } } - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "trace")] fn tys(&mut self, pattern: Ty<'tcx>, value: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { // FIXME(non_lifetime_binders): What to do here? if matches!(pattern.kind(), ty::Error(_) | ty::Bound(..)) { @@ -185,13 +180,12 @@ impl<'tcx> TypeRelation> for MatchAgainstHigherRankedOutlives<'tcx> } } - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "trace")] fn consts( &mut self, pattern: ty::Const<'tcx>, value: ty::Const<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> { - debug!("{}.consts({:?}, {:?})", self.tag(), pattern, value); if pattern == value { Ok(pattern) } else { @@ -199,6 +193,7 @@ impl<'tcx> TypeRelation> for MatchAgainstHigherRankedOutlives<'tcx> } } + #[instrument(skip(self), level = "trace")] fn binders( &mut self, pattern: ty::Binder<'tcx, T>, diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index c93b89756f936..1dc03de4c8ba0 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -79,6 +79,7 @@ impl<'tcx> InferCtxt<'tcx> { where R: PredicateEmittingRelation>, { + debug!("super_combine_tys::<{}>({:?}, {:?})", std::any::type_name::(), a, b); debug_assert!(!a.has_escaping_bound_vars()); debug_assert!(!b.has_escaping_bound_vars()); @@ -174,9 +175,10 @@ impl<'tcx> InferCtxt<'tcx> { where R: PredicateEmittingRelation>, { - debug!("{}.consts({:?}, {:?})", relation.tag(), a, b); + debug!("super_combine_consts::<{}>({:?}, {:?})", std::any::type_name::(), a, b); debug_assert!(!a.has_escaping_bound_vars()); debug_assert!(!b.has_escaping_bound_vars()); + if a == b { return Ok(a); } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index fe3b8d60fb91b..30cfbcae6b263 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -372,7 +372,7 @@ impl<'tcx> Generalizer<'_, 'tcx> { let is_nested_alias = mem::replace(&mut self.in_alias, true); let result = match self.relate(alias, alias) { - Ok(alias) => Ok(alias.to_ty(self.tcx())), + Ok(alias) => Ok(alias.to_ty(self.cx())), Err(e) => { if is_nested_alias { return Err(e); @@ -397,14 +397,10 @@ impl<'tcx> Generalizer<'_, 'tcx> { } impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } - fn tag(&self) -> &'static str { - "Generalizer" - } - fn relate_item_args( &mut self, item_def_id: DefId, @@ -417,7 +413,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { // (e.g., #41849). relate::relate_args_invariantly(self, a_arg, b_arg) } else { - let tcx = self.tcx(); + let tcx = self.cx(); let opt_variances = tcx.variances_of(item_def_id); relate::relate_args_with_variances( self, @@ -525,7 +521,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { } debug!("replacing original vid={:?} with new={:?}", vid, new_var_id); - Ok(Ty::new_var(self.tcx(), new_var_id)) + Ok(Ty::new_var(self.cx(), new_var_id)) } } } @@ -654,7 +650,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { { variable_table.union(vid, new_var_id); } - Ok(ty::Const::new_var(self.tcx(), new_var_id)) + Ok(ty::Const::new_var(self.cx(), new_var_id)) } } } @@ -672,7 +668,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { args, args, )?; - Ok(ty::Const::new_unevaluated(self.tcx(), ty::UnevaluatedConst { def, args })) + Ok(ty::Const::new_unevaluated(self.cx(), ty::UnevaluatedConst { def, args })) } ty::ConstKind::Placeholder(placeholder) => { if self.for_universe.can_name(placeholder.universe) { diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs index 5bb8a113e173e..4f2cf2c43e7cc 100644 --- a/compiler/rustc_infer/src/infer/relate/glb.rs +++ b/compiler/rustc_infer/src/infer/relate/glb.rs @@ -23,11 +23,7 @@ impl<'combine, 'infcx, 'tcx> Glb<'combine, 'infcx, 'tcx> { } impl<'tcx> TypeRelation> for Glb<'_, '_, 'tcx> { - fn tag(&self) -> &'static str { - "Glb" - } - - fn tcx(&self) -> TyCtxt<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { self.fields.tcx() } @@ -47,27 +43,28 @@ impl<'tcx> TypeRelation> for Glb<'_, '_, 'tcx> { } } + #[instrument(skip(self), level = "trace")] fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { lattice::super_lattice_tys(self, a, b) } + #[instrument(skip(self), level = "trace")] fn regions( &mut self, a: ty::Region<'tcx>, b: ty::Region<'tcx>, ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!("{}.regions({:?}, {:?})", self.tag(), a, b); - let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8 Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().lub_regions( - self.tcx(), + self.cx(), origin, a, b, )) } + #[instrument(skip(self), level = "trace")] fn consts( &mut self, a: ty::Const<'tcx>, diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index 46e7466141a58..d1d870715622f 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -56,8 +56,6 @@ pub fn super_lattice_tys<'a, 'tcx: 'a, L>( where L: LatticeDir<'a, 'tcx>, { - debug!("{}", this.tag()); - if a == b { return Ok(a); } diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs index 94c1464817f73..2eb20f311cf4a 100644 --- a/compiler/rustc_infer/src/infer/relate/lub.rs +++ b/compiler/rustc_infer/src/infer/relate/lub.rs @@ -23,11 +23,7 @@ impl<'combine, 'infcx, 'tcx> Lub<'combine, 'infcx, 'tcx> { } impl<'tcx> TypeRelation> for Lub<'_, '_, 'tcx> { - fn tag(&self) -> &'static str { - "Lub" - } - - fn tcx(&self) -> TyCtxt<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { self.fields.tcx() } @@ -51,23 +47,23 @@ impl<'tcx> TypeRelation> for Lub<'_, '_, 'tcx> { lattice::super_lattice_tys(self, a, b) } + #[instrument(skip(self), level = "trace")] fn regions( &mut self, a: ty::Region<'tcx>, b: ty::Region<'tcx>, ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!("{}.regions({:?}, {:?})", self.tag(), a, b); - let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8 Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().glb_regions( - self.tcx(), + self.cx(), origin, a, b, )) } + #[instrument(skip(self), level = "trace")] fn consts( &mut self, a: ty::Const<'tcx>, diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index e206f530519b5..3fe3535428626 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -28,11 +28,7 @@ impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> { } impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { - fn tag(&self) -> &'static str { - "TypeRelating" - } - - fn tcx(&self) -> TyCtxt<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { self.fields.infcx.tcx } @@ -48,7 +44,7 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { // (e.g., #41849). relate_args_invariantly(self, a_arg, b_arg) } else { - let tcx = self.tcx(); + let tcx = self.cx(); let opt_variances = tcx.variances_of(item_def_id); relate_args_with_variances(self, item_def_id, opt_variances, a_arg, b_arg, false) } @@ -71,7 +67,7 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { r } - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "trace")] fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { if a == b { return Ok(a); @@ -88,7 +84,7 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { // can't make progress on `A <: B` if both A and B are // type variables, so record an obligation. self.fields.goals.push(Goal::new( - self.tcx(), + self.cx(), self.fields.param_env, ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: true, @@ -101,7 +97,7 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { // can't make progress on `B <: A` if both A and B are // type variables, so record an obligation. self.fields.goals.push(Goal::new( - self.tcx(), + self.cx(), self.fields.param_env, ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: false, @@ -134,7 +130,7 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { (&ty::Error(e), _) | (_, &ty::Error(e)) => { infcx.set_tainted_by_errors(e); - return Ok(Ty::new_error(self.tcx(), e)); + return Ok(Ty::new_error(self.cx(), e)); } ( @@ -166,12 +162,12 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { Ok(a) } + #[instrument(skip(self), level = "trace")] fn regions( &mut self, a: ty::Region<'tcx>, b: ty::Region<'tcx>, ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!("{}.regions({:?}, {:?})", self.tag(), a, b); let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); match self.ambient_variance { @@ -209,6 +205,7 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { Ok(a) } + #[instrument(skip(self), level = "trace")] fn consts( &mut self, a: ty::Const<'tcx>, diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index b65ac8596675a..02ebf933f5329 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -34,6 +34,7 @@ #[macro_use] extern crate tracing; +pub mod error_reporting; mod errors; pub mod infer; pub mod traits; diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 4b3b0728f385e..b5abf145d6b5b 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -53,6 +53,11 @@ tracing = "0.1" [features] # tidy-alphabetical-start -llvm = ['rustc_codegen_llvm'] -rustc_use_parallel_compiler = ['rustc-rayon', 'rustc-rayon-core', 'rustc_query_impl/rustc_use_parallel_compiler', 'rustc_errors/rustc_use_parallel_compiler'] +llvm = ['dep:rustc_codegen_llvm'] +rustc_use_parallel_compiler = [ + 'dep:rustc-rayon', + 'dep:rustc-rayon-core', + 'rustc_query_impl/rustc_use_parallel_compiler', + 'rustc_errors/rustc_use_parallel_compiler' +] # tidy-alphabetical-end diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index de04d882f5163..7d7b97e2eb1a3 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -403,8 +403,9 @@ lint_inner_macro_attribute_unstable = inner macro attributes are unstable lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly .label = use a different label that doesn't start with `0` or `1` - .note = an LLVM bug makes these labels ambiguous with a binary literal number - .note = see for more information + .help = start numbering with `2` instead + .note1 = an LLVM bug makes these labels ambiguous with a binary literal number on x86 + .note2 = see for more information lint_invalid_asm_label_format_arg = avoid using named labels in inline assembly .help = only local labels of the form `:` should be used in inline asm @@ -556,6 +557,9 @@ lint_non_fmt_panic_unused = } .add_fmt_suggestion = or add a "{"{"}{"}"}" format string to use the message literally +lint_non_glob_import_type_ir_inherent = non-glob import of `rustc_type_ir::inherent` + .suggestion = try using a glob import instead + lint_non_local_definitions_cargo_update = the {$macro_kind} `{$macro_name}` may come from an old version of the `{$crate_name}` crate, try updating your dependency with `cargo update -p {$crate_name}` lint_non_local_definitions_deprecation = this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 485c214ac9def..9ebada0fff377 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -66,6 +66,7 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, InnerSpan, Span}; use rustc_target::abi::Abi; +use rustc_target::asm::InlineAsmArch; use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{self, misc::type_allowed_to_implement_copy}; @@ -2739,8 +2740,9 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail - /// # #![feature(asm_experimental_arch)] + /// ```rust,ignore (fails on non-x86_64) + /// #![cfg(target_arch = "x86_64")] + /// /// use std::arch::asm; /// /// fn main() { @@ -2750,19 +2752,32 @@ declare_lint! { /// } /// ``` /// - /// {{produces}} + /// This will produce: + /// + /// ```text + /// error: avoid using labels containing only the digits `0` and `1` in inline assembly + /// --> :7:15 + /// | + /// 7 | asm!("0: jmp 0b"); + /// | ^ use a different label that doesn't start with `0` or `1` + /// | + /// = help: start numbering with `2` instead + /// = note: an LLVM bug makes these labels ambiguous with a binary literal number on x86 + /// = note: see for more information + /// = note: `#[deny(binary_asm_labels)]` on by default + /// ``` /// /// ### Explanation /// - /// A [LLVM bug] causes this code to fail to compile because it interprets the `0b` as a binary - /// literal instead of a reference to the previous local label `0`. Note that even though the - /// bug is marked as fixed, it only fixes a specific usage of intel syntax within standalone - /// files, not inline assembly. To work around this bug, don't use labels that could be - /// confused with a binary literal. + /// An [LLVM bug] causes this code to fail to compile because it interprets the `0b` as a binary + /// literal instead of a reference to the previous local label `0`. To work around this bug, + /// don't use labels that could be confused with a binary literal. + /// + /// This behavior is platform-specific to x86 and x86-64. /// /// See the explanation in [Rust By Example] for more details. /// - /// [LLVM bug]: https://bugs.llvm.org/show_bug.cgi?id=36144 + /// [LLVM bug]: https://github.com/llvm/llvm-project/issues/99547 /// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels pub BINARY_ASM_LABELS, Deny, @@ -2908,16 +2923,22 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels { InvalidAsmLabel::FormatArg { missing_precise_span }, ); } - AsmLabelKind::Binary => { - // the binary asm issue only occurs when using intel syntax - if !options.contains(InlineAsmOptions::ATT_SYNTAX) { - cx.emit_span_lint( - BINARY_ASM_LABELS, - span, - InvalidAsmLabel::Binary { missing_precise_span, span }, - ) - } + // the binary asm issue only occurs when using intel syntax on x86 targets + AsmLabelKind::Binary + if !options.contains(InlineAsmOptions::ATT_SYNTAX) + && matches!( + cx.tcx.sess.asm_arch, + Some(InlineAsmArch::X86 | InlineAsmArch::X86_64) | None + ) => + { + cx.emit_span_lint( + BINARY_ASM_LABELS, + span, + InvalidAsmLabel::Binary { missing_precise_span, span }, + ) } + // No lint on anything other than x86 + AsmLabelKind::Binary => (), }; } } diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 772cc2ff8b997..e15eb90f82708 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -3,7 +3,8 @@ use crate::lints::{ BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistentDocKeyword, - QueryInstability, SpanUseEqCtxtDiag, TyQualified, TykindDiag, TykindKind, UntranslatableDiag, + NonGlobImportTypeIrInherent, QueryInstability, SpanUseEqCtxtDiag, TyQualified, TykindDiag, + TykindKind, UntranslatableDiag, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_ast as ast; @@ -263,6 +264,49 @@ fn gen_args(segment: &PathSegment<'_>) -> String { String::new() } +declare_tool_lint! { + /// The `non_glob_import_of_type_ir_inherent_item` lint detects + /// non-glob imports of module `rustc_type_ir::inherent`. + pub rustc::NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, + Allow, + "non-glob import of `rustc_type_ir::inherent`", + report_in_external_macro: true +} + +declare_lint_pass!(TypeIr => [NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT]); + +impl<'tcx> LateLintPass<'tcx> for TypeIr { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + let rustc_hir::ItemKind::Use(path, kind) = item.kind else { return }; + + let is_mod_inherent = |def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id); + let (lo, hi, snippet) = match path.segments { + [.., penultimate, segment] + if penultimate.res.opt_def_id().is_some_and(is_mod_inherent) => + { + (segment.ident.span, item.ident.span, "*") + } + [.., segment] + if path.res.iter().flat_map(Res::opt_def_id).any(is_mod_inherent) + && let rustc_hir::UseKind::Single = kind => + { + let (lo, snippet) = + match cx.tcx.sess.source_map().span_to_snippet(path.span).as_deref() { + Ok("self") => (path.span, "*"), + _ => (segment.ident.span.shrink_to_hi(), "::*"), + }; + (lo, if segment.ident == item.ident { lo } else { item.ident.span }, snippet) + } + _ => return, + }; + cx.emit_span_lint( + NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, + path.span, + NonGlobImportTypeIrInherent { suggestion: lo.eq_ctxt(hi).then(|| lo.to(hi)), snippet }, + ); + } +} + declare_tool_lint! { /// The `lint_pass_impl_without_macro` detects manual implementations of a lint /// pass, without using [`declare_lint_pass`] or [`impl_lint_pass`]. diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 8be8996e4c8f0..290f91045c455 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -120,7 +120,6 @@ use types::*; use unit_bindings::*; use unused::*; -/// Useful for other parts of the compiler / Clippy. pub use builtin::{MissingDoc, SoftLints}; pub use context::{CheckLintNameResult, FindLintError, LintStore}; pub use context::{EarlyContext, LateContext, LintContext}; @@ -573,6 +572,8 @@ fn register_internals(store: &mut LintStore) { store.register_late_mod_pass(|_| Box::new(ExistingDocKeyword)); store.register_lints(&TyTyKind::get_lints()); store.register_late_mod_pass(|_| Box::new(TyTyKind)); + store.register_lints(&TypeIr::get_lints()); + store.register_late_mod_pass(|_| Box::new(TypeIr)); store.register_lints(&Diagnostics::get_lints()); store.register_late_mod_pass(|_| Box::new(Diagnostics)); store.register_lints(&BadOptAccess::get_lints()); @@ -596,6 +597,7 @@ fn register_internals(store: &mut LintStore) { LintId::of(PASS_BY_VALUE), LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), LintId::of(USAGE_OF_QUALIFIED_TY), + LintId::of(NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT), LintId::of(EXISTING_DOC_KEYWORD), LintId::of(BAD_OPT_ACCESS), LintId::of(SPAN_USE_EQ_CTXT), diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 308bb73f4cea6..6c5f366727f95 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -926,6 +926,14 @@ pub struct TyQualified { pub suggestion: Span, } +#[derive(LintDiagnostic)] +#[diag(lint_non_glob_import_type_ir_inherent)] +pub struct NonGlobImportTypeIrInherent { + #[suggestion(code = "{snippet}", applicability = "maybe-incorrect")] + pub suggestion: Option, + pub snippet: &'static str, +} + #[derive(LintDiagnostic)] #[diag(lint_lintpass_by_hand)] #[help] @@ -2066,7 +2074,9 @@ pub enum InvalidAsmLabel { missing_precise_span: bool, }, #[diag(lint_invalid_asm_label_binary)] - #[note] + #[help] + #[note(lint_note1)] + #[note(lint_note2)] Binary { #[note(lint_invalid_asm_label_no_span)] missing_precise_span: bool, diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index c1f5cd45dc824..fa23f120468a4 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -78,7 +78,7 @@ fn gen_args(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> String { .tcx .sess .source_map() - .span_to_snippet(c.value.span) + .span_to_snippet(c.span()) .unwrap_or_else(|_| "_".into()), GenericArg::Infer(_) => String::from("_"), }) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index aa7844f40121b..04764b71b1002 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1203,16 +1203,16 @@ declare_lint! { /// This was historically allowed, but is not the intended behavior /// according to the visibility rules. This is a [future-incompatible] /// lint to transition this to a hard error in the future. See [issue - /// #34537] for more details. + /// #127909] for more details. /// - /// [issue #34537]: https://github.com/rust-lang/rust/issues/34537 + /// [issue #127909]: https://github.com/rust-lang/rust/issues/127909 /// [future-incompatible]: ../index.md#future-incompatible-lints pub PUB_USE_OF_PRIVATE_EXTERN_CRATE, Deny, "detect public re-exports of private extern crates", @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, - reference: "issue #34537 ", + reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, + reference: "issue #127909 ", }; } diff --git a/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp index a9d1362a338d0..ccf1a5429e2b5 100644 --- a/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp @@ -4,12 +4,16 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // Derived from: -// * https://github.com/llvm/llvm-project/blob/8ef3e895ad8ab1724e2b87cabad1dacdc7a397a3/llvm/include/llvm/Object/ArchiveWriter.h -// * https://github.com/llvm/llvm-project/blob/8ef3e895ad8ab1724e2b87cabad1dacdc7a397a3/llvm/lib/Object/ArchiveWriter.cpp +// * https://github.com/llvm/llvm-project/blob/ef6d1ec07c693352c4a60dd58db08d2d8558f6ea/llvm/include/llvm/Object/ArchiveWriter.h +// * https://github.com/llvm/llvm-project/blob/ef6d1ec07c693352c4a60dd58db08d2d8558f6ea/llvm/lib/Object/ArchiveWriter.cpp +#include "LLVMWrapper.h" #include "SuppressLLVMWarnings.h" #include "llvm/ADT/SmallString.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/COFFImportFile.h" +#include "llvm/Object/IRObjectFile.h" #include "llvm/Object/ObjectFile.h" #include @@ -34,6 +38,27 @@ static bool isArchiveSymbol(const object::BasicSymbolRef &S) { typedef void *(*LLVMRustGetSymbolsCallback)(void *, const char *); typedef void *(*LLVMRustGetSymbolsErrorCallback)(const char *); +// This function is copied from ArchiveWriter.cpp. +static Expected> +getSymbolicFile(MemoryBufferRef Buf, LLVMContext &Context) { + const file_magic Type = identify_magic(Buf.getBuffer()); + // Don't attempt to read non-symbolic file types. + if (!object::SymbolicFile::isSymbolicFile(Type, &Context)) + return nullptr; + if (Type == file_magic::bitcode) { + auto ObjOrErr = object::SymbolicFile::createSymbolicFile( + Buf, file_magic::bitcode, &Context); + if (!ObjOrErr) + return ObjOrErr.takeError(); + return std::move(*ObjOrErr); + } else { + auto ObjOrErr = object::SymbolicFile::createSymbolicFile(Buf); + if (!ObjOrErr) + return ObjOrErr.takeError(); + return std::move(*ObjOrErr); + } +} + // Note: This is implemented in C++ instead of using the C api from Rust as // IRObjectFile doesn't implement getSymbolName, only printSymbolName, which is // inaccessible from the C api. @@ -49,36 +74,16 @@ LLVMRustGetSymbols(char *BufPtr, size_t BufLen, void *State, // In the scenario when LLVMContext is populated SymbolicFile will contain a // reference to it, thus SymbolicFile should be destroyed first. LLVMContext Context; - std::unique_ptr Obj; - - const file_magic Type = identify_magic(Buf->getBuffer()); - if (!object::SymbolicFile::isSymbolicFile(Type, &Context)) { - return 0; - } - - if (Type == file_magic::bitcode) { - auto ObjOrErr = object::SymbolicFile::createSymbolicFile( - Buf->getMemBufferRef(), file_magic::bitcode, &Context); - if (!ObjOrErr) { - Error E = ObjOrErr.takeError(); - SmallString<0> ErrorBuf; - auto Error = raw_svector_ostream(ErrorBuf); - Error << E << '\0'; - return ErrorCallback(Error.str().data()); - } - Obj = std::move(*ObjOrErr); - } else { - auto ObjOrErr = - object::SymbolicFile::createSymbolicFile(Buf->getMemBufferRef()); - if (!ObjOrErr) { - Error E = ObjOrErr.takeError(); - SmallString<0> ErrorBuf; - auto Error = raw_svector_ostream(ErrorBuf); - Error << E << '\0'; - return ErrorCallback(Error.str().data()); - } - Obj = std::move(*ObjOrErr); + Expected> ObjOrErr = + getSymbolicFile(Buf->getMemBufferRef(), Context); + if (!ObjOrErr) { + Error E = ObjOrErr.takeError(); + SmallString<0> ErrorBuf; + auto Error = raw_svector_ostream(ErrorBuf); + Error << E << '\0'; + return ErrorCallback(Error.str().data()); } + std::unique_ptr Obj = std::move(*ObjOrErr); for (const object::BasicSymbolRef &S : Obj->symbols()) { if (!isArchiveSymbol(S)) @@ -97,3 +102,72 @@ LLVMRustGetSymbols(char *BufPtr, size_t BufLen, void *State, } return 0; } + +// Encoding true and false as invalid pointer values +#define TRUE_PTR (void *)1 +#define FALSE_PTR (void *)0 + +extern "C" bool LLVMRustIs64BitSymbolicFile(char *BufPtr, size_t BufLen) { + std::unique_ptr Buf = MemoryBuffer::getMemBuffer( + StringRef(BufPtr, BufLen), StringRef("LLVMRustGetSymbolsObject"), false); + SmallString<0> SymNameBuf; + auto SymName = raw_svector_ostream(SymNameBuf); + + // Code starting from this line is copied from s64BitSymbolicFile in + // ArchiveWriter.cpp. + // In the scenario when LLVMContext is populated SymbolicFile will contain a + // reference to it, thus SymbolicFile should be destroyed first. + LLVMContext Context; + Expected> ObjOrErr = + getSymbolicFile(Buf->getMemBufferRef(), Context); + if (!ObjOrErr) { + return false; + } + std::unique_ptr Obj = std::move(*ObjOrErr); + + return Obj != nullptr ? Obj->is64Bit() : false; +} + +extern "C" bool LLVMRustIsECObject(char *BufPtr, size_t BufLen) { + std::unique_ptr Buf = MemoryBuffer::getMemBuffer( + StringRef(BufPtr, BufLen), StringRef("LLVMRustGetSymbolsObject"), false); + SmallString<0> SymNameBuf; + auto SymName = raw_svector_ostream(SymNameBuf); + + // In the scenario when LLVMContext is populated SymbolicFile will contain a + // reference to it, thus SymbolicFile should be destroyed first. + LLVMContext Context; + Expected> ObjOrErr = + getSymbolicFile(Buf->getMemBufferRef(), Context); + if (!ObjOrErr) { + return false; + } + std::unique_ptr Obj = std::move(*ObjOrErr); + + if (Obj == nullptr) { + return false; + } + + // Code starting from this line is copied from isECObject in + // ArchiveWriter.cpp with an extra #if to work with LLVM 17. + if (Obj->isCOFF()) + return cast(&*Obj)->getMachine() != + COFF::IMAGE_FILE_MACHINE_ARM64; + +#if LLVM_VERSION_GE(18, 0) + if (Obj->isCOFFImportFile()) + return cast(&*Obj)->getMachine() != + COFF::IMAGE_FILE_MACHINE_ARM64; +#endif + + if (Obj->isIR()) { + Expected TripleStr = + getBitcodeTargetTriple(Obj->getMemoryBufferRef()); + if (!TripleStr) + return false; + Triple T(*TripleStr); + return T.isWindowsArm64EC() || T.getArch() == Triple::x86_64; + } + + return false; +} diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index e5366c9b51830..1365afbce1c37 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -33,7 +33,7 @@ pub unsafe extern "C" fn LLVMRustStringWriteImpl( ptr: *const c_char, size: size_t, ) { - let slice = slice::from_raw_parts(ptr as *const u8, size); + let slice = unsafe { slice::from_raw_parts(ptr as *const u8, size) }; sr.bytes.borrow_mut().extend_from_slice(slice); } diff --git a/compiler/rustc_macros/src/lift.rs b/compiler/rustc_macros/src/lift.rs index d41ceb29816cc..627f4088d5f5c 100644 --- a/compiler/rustc_macros/src/lift.rs +++ b/compiler/rustc_macros/src/lift.rs @@ -45,7 +45,7 @@ pub fn lift_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStre quote! { type Lifted = #lifted; - fn lift_to_tcx(self, __tcx: ::rustc_middle::ty::TyCtxt<'__lifted>) -> Option<#lifted> { + fn lift_to_interner(self, __tcx: ::rustc_middle::ty::TyCtxt<'__lifted>) -> Option<#lifted> { Some(match self { #body }) } }, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 3dc592980fdb6..290ebde871282 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -40,5 +40,5 @@ tracing = "0.1" [features] # tidy-alphabetical-start -rustc_use_parallel_compiler = ["rustc-rayon-core"] +rustc_use_parallel_compiler = ["dep:rustc-rayon-core"] # tidy-alphabetical-end diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 2f3a6ee601b15..ad59bfa904729 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -912,6 +912,7 @@ impl<'hir> Map<'hir> { Node::Field(field) => field.span, Node::AnonConst(constant) => constant.span, Node::ConstBlock(constant) => self.body(constant.body).value.span, + Node::ConstArg(const_arg) => const_arg.span(), Node::Expr(expr) => expr.span, Node::ExprField(field) => field.span, Node::Stmt(stmt) => stmt.span, @@ -962,7 +963,8 @@ 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.tcx.parent_hir_node(anon_const) { + let const_arg = self.tcx.parent_hir_id(anon_const); + match self.tcx.parent_hir_node(const_arg) { Node::GenericParam(GenericParam { def_id: param_id, kind: GenericParamKind::Const { .. }, @@ -1182,6 +1184,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { } Node::AnonConst(_) => node_str("const"), Node::ConstBlock(_) => node_str("const"), + Node::ConstArg(_) => node_str("const"), Node::Expr(_) => node_str("expr"), Node::ExprField(_) => node_str("expr field"), Node::Stmt(_) => node_str("stmt"), diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index fcea1ea81a7c4..d385be007d33f 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -59,7 +59,7 @@ macro_rules! TrivialLiftImpls { $( impl<'tcx> $crate::ty::Lift<$crate::ty::TyCtxt<'tcx>> for $ty { type Lifted = Self; - fn lift_to_tcx(self, _: $crate::ty::TyCtxt<'tcx>) -> Option { + fn lift_to_interner(self, _: $crate::ty::TyCtxt<'tcx>) -> Option { Some(self) } } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 6a8498abaf933..9df19565ab383 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -90,9 +90,11 @@ TrivialTypeTraversalImpls! { ErrorHandled } pub type EvalToAllocationRawResult<'tcx> = Result, ErrorHandled>; pub type EvalStaticInitializerRawResult<'tcx> = Result, ErrorHandled>; pub type EvalToConstValueResult<'tcx> = Result, ErrorHandled>; -/// `Ok(None)` indicates the constant was fine, but the valtree couldn't be constructed. -/// This is needed in `thir::pattern::lower_inline_const`. -pub type EvalToValTreeResult<'tcx> = Result>, ErrorHandled>; +/// `Ok(Err(ty))` indicates the constant was fine, but the valtree couldn't be constructed +/// because the value containts something of type `ty` that is not valtree-compatible. +/// The caller can then show an appropriate error; the query does not have the +/// necssary context to give good user-facing errors for this case. +pub type EvalToValTreeResult<'tcx> = Result, Ty<'tcx>>, ErrorHandled>; #[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8); diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 301c9911b44cd..d9fa5b02f7f74 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -157,9 +157,10 @@ impl EraseType for Result, mir::interpret::ErrorHandled> { type Result = [u8; size_of::, mir::interpret::ErrorHandled>>()]; } -impl EraseType for Result>, mir::interpret::ErrorHandled> { - type Result = - [u8; size_of::>, mir::interpret::ErrorHandled>>()]; +impl EraseType for Result, Ty<'_>>, mir::interpret::ErrorHandled> { + type Result = [u8; size_of::< + Result, Ty<'static>>, mir::interpret::ErrorHandled>, + >()]; } impl EraseType for Result<&'_ ty::List>, ty::util::AlwaysRequiresDrop> { diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index c97af68c29e5f..b80d00719ee5e 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -783,16 +783,13 @@ pub enum PatKind<'tcx> { }, /// One of the following: - /// * `&str` (represented as a valtree), which will be handled as a string pattern and thus - /// exhaustiveness checking will detect if you use the same string twice in different - /// patterns. + /// * `&str`/`&[u8]` (represented as a valtree), which will be handled as a string/slice pattern + /// and thus exhaustiveness checking will detect if you use the same string/slice twice in + /// different patterns. /// * integer, bool, char or float (represented as a valtree), which will be handled by /// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are /// much simpler. - /// * Opaque constants (represented as `mir::ConstValue`), that must not be matched - /// structurally. So anything that does not derive `PartialEq` and `Eq`. - /// - /// These are always compared with the matched place using (the semantics of) `PartialEq`. + /// * `String`, if `string_deref_patterns` is enabled. Constant { value: mir::Const<'tcx>, }, diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 32d01d07c17e7..5cf1247f0c820 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -1,11 +1,12 @@ use crate::middle::resolve_bound_vars as rbv; use crate::mir::interpret::{ErrorHandled, LitToConstInput, Scalar}; use crate::ty::{self, GenericArgs, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; +use either::Either; use rustc_data_structures::intern::Interned; use rustc_error_messages::MultiSpan; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{self as hir, HirId}; use rustc_macros::HashStable; use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo}; use tracing::{debug, instrument}; @@ -182,16 +183,55 @@ impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> { } } +/// In some cases, [`hir::ConstArg`]s that are being used in the type system +/// through const generics need to have their type "fed" to them +/// using the query system. +/// +/// Use this enum with [`Const::from_const_arg`] to instruct it with the +/// desired behavior. +#[derive(Debug, Clone, Copy)] +pub enum FeedConstTy { + /// Feed the type. + /// + /// The `DefId` belongs to the const param that we are supplying + /// this (anon) const arg to. + Param(DefId), + /// Don't feed the type. + No, +} + impl<'tcx> Const<'tcx> { + /// Convert a [`hir::ConstArg`] to a [`ty::Const`](Self). + #[instrument(skip(tcx), level = "debug")] + pub fn from_const_arg( + tcx: TyCtxt<'tcx>, + const_arg: &'tcx hir::ConstArg<'tcx>, + feed: FeedConstTy, + ) -> Self { + if let FeedConstTy::Param(param_def_id) = feed + && let hir::ConstArgKind::Anon(anon) = &const_arg.kind + { + tcx.feed_anon_const_type(anon.def_id, tcx.type_of(param_def_id)); + } + + match const_arg.kind { + hir::ConstArgKind::Path(qpath) => { + // FIXME(min_generic_const_args): for now only params are lowered to ConstArgKind::Path + Self::from_param(tcx, qpath, const_arg.hir_id) + } + hir::ConstArgKind::Anon(anon) => Self::from_anon_const(tcx, anon.def_id), + } + } + /// Literals and const generic parameters are eagerly converted to a constant, everything else /// becomes `Unevaluated`. #[instrument(skip(tcx), level = "debug")] pub fn from_anon_const(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Self { let body_id = match tcx.hir_node_by_def_id(def) { hir::Node::AnonConst(ac) => ac.body, - _ => span_bug!( + node => span_bug!( tcx.def_span(def.to_def_id()), - "from_anon_const can only process anonymous constants" + "from_anon_const can only process anonymous constants, not {node:?}" ), }; @@ -200,7 +240,7 @@ impl<'tcx> Const<'tcx> { let ty = tcx.type_of(def).no_bound_vars().expect("const parameter types cannot be generic"); - match Self::try_from_lit_or_param(tcx, ty, expr) { + match Self::try_from_lit(tcx, ty, expr) { Some(v) => v, None => ty::Const::new_unevaluated( tcx, @@ -212,12 +252,36 @@ impl<'tcx> Const<'tcx> { } } + /// Lower a const param to a [`Const`]. + /// + /// IMPORTANT: `qpath` must be a const param, otherwise this will panic + fn from_param(tcx: TyCtxt<'tcx>, qpath: hir::QPath<'tcx>, hir_id: HirId) -> Self { + let hir::QPath::Resolved(_, &hir::Path { res: Res::Def(DefKind::ConstParam, def_id), .. }) = + qpath + else { + span_bug!(qpath.span(), "non-param {qpath:?} passed to Const::from_param") + }; + + match tcx.named_bound_var(hir_id) { + Some(rbv::ResolvedArg::EarlyBound(_)) => { + // Find the name and index of the const parameter by indexing the generics of + // the parent item and construct a `ParamConst`. + let item_def_id = tcx.parent(def_id); + let generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id]; + let name = tcx.item_name(def_id); + ty::Const::new_param(tcx, ty::ParamConst::new(index, name)) + } + Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => { + ty::Const::new_bound(tcx, debruijn, ty::BoundVar::from_u32(index)) + } + Some(rbv::ResolvedArg::Error(guar)) => ty::Const::new_error(tcx, guar), + arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", hir_id), + } + } + #[instrument(skip(tcx), level = "debug")] - fn try_from_lit_or_param( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Option { + fn try_from_lit(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, expr: &'tcx hir::Expr<'tcx>) -> Option { // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments // currently have to be wrapped in curly brackets, so it's necessary to special-case. let expr = match &expr.kind { @@ -250,34 +314,15 @@ impl<'tcx> Const<'tcx> { } } - // FIXME(const_generics): We currently have to special case parameters because `min_const_generics` - // does not provide the parents generics to anonymous constants. We still allow generic const - // parameters by themselves however, e.g. `N`. These constants would cause an ICE if we were to - // ever try to instantiate the generic parameters in their bodies. - match expr.kind { - hir::ExprKind::Path(hir::QPath::Resolved( - _, - &hir::Path { res: Res::Def(DefKind::ConstParam, def_id), .. }, - )) => { - match tcx.named_bound_var(expr.hir_id) { - Some(rbv::ResolvedArg::EarlyBound(_)) => { - // Find the name and index of the const parameter by indexing the generics of - // the parent item and construct a `ParamConst`. - let item_def_id = tcx.parent(def_id); - let generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id]; - let name = tcx.item_name(def_id); - Some(ty::Const::new_param(tcx, ty::ParamConst::new(index, name))) - } - Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => { - Some(ty::Const::new_bound(tcx, debruijn, ty::BoundVar::from_u32(index))) - } - Some(rbv::ResolvedArg::Error(guar)) => Some(ty::Const::new_error(tcx, guar)), - arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", expr.hir_id), - } - } - _ => None, + if let hir::ExprKind::Path(hir::QPath::Resolved( + _, + &hir::Path { res: Res::Def(DefKind::ConstParam, _), .. }, + )) = expr.kind + { + span_bug!(expr.span, "try_from_lit: received const param which shouldn't be possible") } + + None } #[inline] @@ -312,14 +357,16 @@ impl<'tcx> Const<'tcx> { Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize)) } - /// Returns the evaluated constant + /// Returns the evaluated constant as a valtree; + /// if that fails due to a valtree-incompatible type, indicate which type that is + /// by returning `Err(Left(bad_type))`. #[inline] - pub fn eval( + pub fn eval_valtree( self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, span: Span, - ) -> Result<(Ty<'tcx>, ValTree<'tcx>), ErrorHandled> { + ) -> Result<(Ty<'tcx>, ValTree<'tcx>), Either, ErrorHandled>> { assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}"); match self.kind() { ConstKind::Unevaluated(unevaluated) => { @@ -328,27 +375,47 @@ impl<'tcx> Const<'tcx> { let (param_env, unevaluated) = unevaluated.prepare_for_eval(tcx, param_env); // try to resolve e.g. associated constants to their definition on an impl, and then // evaluate the const. - let Some(c) = tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span)? - else { - // This can happen when we run on ill-typed code. - let e = tcx.dcx().span_delayed_bug( - span, - "`ty::Const::eval` called on a non-valtree-compatible type", - ); - return Err(e.into()); - }; - Ok((tcx.type_of(unevaluated.def).instantiate(tcx, unevaluated.args), c)) + match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span) { + Ok(Ok(c)) => { + Ok((tcx.type_of(unevaluated.def).instantiate(tcx, unevaluated.args), c)) + } + Ok(Err(bad_ty)) => Err(Either::Left(bad_ty)), + Err(err) => Err(Either::Right(err.into())), + } } ConstKind::Value(ty, val) => Ok((ty, val)), - ConstKind::Error(g) => Err(g.into()), + ConstKind::Error(g) => Err(Either::Right(g.into())), ConstKind::Param(_) | ConstKind::Infer(_) | ConstKind::Bound(_, _) | ConstKind::Placeholder(_) - | ConstKind::Expr(_) => Err(ErrorHandled::TooGeneric(span)), + | ConstKind::Expr(_) => Err(Either::Right(ErrorHandled::TooGeneric(span))), } } + /// Returns the evaluated constant + #[inline] + pub fn eval( + self, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + span: Span, + ) -> Result<(Ty<'tcx>, ValTree<'tcx>), ErrorHandled> { + self.eval_valtree(tcx, param_env, span).map_err(|err| { + match err { + Either::Right(err) => err, + Either::Left(_bad_ty) => { + // This can happen when we run on ill-typed code. + let e = tcx.dcx().span_delayed_bug( + span, + "`ty::Const::eval` called on a non-valtree-compatible type", + ); + e.into() + } + } + }) + } + /// Normalizes the constant to a value or an error if possible. #[inline] pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { @@ -460,15 +527,15 @@ pub fn const_param_default<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, ) -> ty::EarlyBinder<'tcx, Const<'tcx>> { - let default_def_id = match tcx.hir_node_by_def_id(def_id) { + let default_ct = match tcx.hir_node_by_def_id(def_id) { hir::Node::GenericParam(hir::GenericParam { - kind: hir::GenericParamKind::Const { default: Some(ac), .. }, + kind: hir::GenericParamKind::Const { default: Some(ct), .. }, .. - }) => ac.def_id, + }) => ct, _ => span_bug!( tcx.def_span(def_id), "`const_param_default` expected a generic parameter with a constant" ), }; - ty::EarlyBinder::bind(Const::from_anon_const(tcx, default_def_id)) + ty::EarlyBinder::bind(Const::from_const_arg(tcx, default_ct, FeedConstTy::No)) } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 25070e6b042c9..fd41668ae44c8 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1484,7 +1484,7 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn lift>>(self, value: T) -> Option { - value.lift_to_tcx(self) + value.lift_to_interner(self) } /// Creates a type context. To use the context call `fn enter` which @@ -2087,7 +2087,7 @@ macro_rules! nop_lift { ($set:ident; $ty:ty => $lifted:ty) => { impl<'a, 'tcx> Lift> for $ty { type Lifted = $lifted; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { // Assert that the set has the right type. // Given an argument that has an interned type, the return type has the type of // the corresponding interner set. This won't actually return anything, we're @@ -2122,7 +2122,7 @@ macro_rules! nop_list_lift { ($set:ident; $ty:ty => $lifted:ty) => { impl<'a, 'tcx> Lift> for &'a List<$ty> { type Lifted = &'tcx List<$lifted>; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { // Assert that the set has the right type. if false { let _x: &InternedSet<'tcx, List<$lifted>> = &tcx.interners.$set; @@ -2160,7 +2160,7 @@ macro_rules! nop_slice_lift { ($ty:ty => $lifted:ty) => { impl<'a, 'tcx> Lift> for &'a [$ty] { type Lifted = &'tcx [$lifted]; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { if self.is_empty() { return Some(&[]); } diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs index 5e256dc8d26e2..7b5ccae35682e 100644 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ b/compiler/rustc_middle/src/ty/context/tls.rs @@ -67,7 +67,7 @@ fn erase(context: &ImplicitCtxt<'_, '_>) -> *const () { #[inline] unsafe fn downcast<'a, 'tcx>(context: *const ()) -> &'a ImplicitCtxt<'a, 'tcx> { - &*(context as *const ImplicitCtxt<'a, 'tcx>) + unsafe { &*(context as *const ImplicitCtxt<'a, 'tcx>) } } /// Sets `context` as the new current `ImplicitCtxt` for the duration of the function `f`. diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 4bf2233799130..f479b18c7c43d 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -271,6 +271,19 @@ pub fn suggest_constraining_type_params<'a>( } } + // in the scenario like impl has stricter requirements than trait, + // we should not suggest restrict bound on the impl, here we double check + // the whether the param already has the constraint by checking `def_id` + let bound_trait_defs: Vec = generics + .bounds_for_param(param.def_id) + .flat_map(|bound| { + bound.bounds.iter().flat_map(|b| b.trait_ref().and_then(|t| t.trait_def_id())) + }) + .collect(); + + constraints + .retain(|(_, def_id)| def_id.map_or(true, |def| !bound_trait_defs.contains(&def))); + if constraints.is_empty() { continue; } @@ -332,6 +345,7 @@ pub fn suggest_constraining_type_params<'a>( // -- // | // replace with: `T: Bar +` + if let Some((span, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) { suggest_restrict(span, true, open_paren_sp); continue; diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index 5ac3168196ad7..10919623de728 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -308,7 +308,7 @@ impl<'tcx> GenericArg<'tcx> { impl<'a, 'tcx> Lift> for GenericArg<'a> { type Lifted = GenericArg<'tcx>; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { match self.unpack() { GenericArgKind::Lifetime(lt) => tcx.lift(lt).map(|lt| lt.into()), GenericArgKind::Type(ty) => tcx.lift(ty).map(|ty| ty.into()), diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 9a4562e9cfc83..bd073cd891f69 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -86,7 +86,7 @@ pub use self::closure::{ CAPTURE_STRUCT_LOCAL, }; pub use self::consts::{ - Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, ValTree, + Const, ConstInt, ConstKind, Expr, ExprKind, FeedConstTy, ScalarInt, UnevaluatedConst, ValTree, }; pub use self::context::{ tls, CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index df080b2887b89..57cd2dc73c41b 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1214,11 +1214,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { && let ty::Alias(_, alias_ty) = self.tcx().fn_sig(fn_def_id).skip_binder().output().skip_binder().kind() && alias_ty.def_id == def_id + && let generics = self.tcx().generics_of(fn_def_id) + // FIXME(return_type_notation): We only support lifetime params for now. + && generics.own_params.iter().all(|param| matches!(param.kind, ty::GenericParamDefKind::Lifetime)) { - let num_args = self.tcx().generics_of(fn_def_id).count(); + let num_args = generics.count(); write!(self, " {{ ")?; self.print_def_path(fn_def_id, &args[..num_args])?; - write!(self, "() }}")?; + write!(self, "(..) }}")?; } Ok(()) diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index ebf0d7ed737fa..61c03922ac05f 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -69,7 +69,7 @@ impl<'tcx> Relate> for ty::Pattern<'tcx> { if inc_a != inc_b { todo!() } - Ok(relation.tcx().mk_pat(ty::PatternKind::Range { start, end, include_end: inc_a })) + Ok(relation.cx().mk_pat(ty::PatternKind::Range { start, end, include_end: inc_a })) } } } @@ -81,7 +81,7 @@ impl<'tcx> Relate> for &'tcx ty::List RelateResult<'tcx, Self> { - let tcx = relation.tcx(); + let tcx = relation.cx(); // FIXME: this is wasteful, but want to do a perf run to see how slow it is. // We need to perform this deduplication as we sometimes generate duplicate projections diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index a9dca47ab4373..7cdc0e32953de 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -257,7 +257,6 @@ TrivialTypeTraversalImpls! { crate::ty::adjustment::PointerCoercion, ::rustc_span::Span, ::rustc_span::symbol::Ident, - ::rustc_errors::ErrorGuaranteed, ty::BoundVar, ty::ValTree<'tcx>, } @@ -284,7 +283,7 @@ TrivialTypeTraversalAndLiftImpls! { impl<'tcx, T: Lift>> Lift> for Option { type Lifted = Option; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { Some(match self { Some(x) => Some(tcx.lift(x)?), None => None, @@ -294,7 +293,7 @@ impl<'tcx, T: Lift>> Lift> for Option { impl<'a, 'tcx> Lift> for Term<'a> { type Lifted = ty::Term<'tcx>; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { match self.unpack() { TermKind::Ty(ty) => tcx.lift(ty).map(Into::into), TermKind::Const(c) => tcx.lift(c).map(Into::into), @@ -443,13 +442,14 @@ impl<'tcx> TypeSuperVisitable> for Ty<'tcx> { pat.visit_with(visitor) } + ty::Error(guar) => guar.visit_with(visitor), + ty::Bool | ty::Char | ty::Str | ty::Int(_) | ty::Uint(_) | ty::Float(_) - | ty::Error(_) | ty::Infer(_) | ty::Bound(..) | ty::Placeholder(..) @@ -602,6 +602,21 @@ impl<'tcx> TypeSuperVisitable> for ty::Const<'tcx> { } } +impl<'tcx> TypeVisitable> for rustc_span::ErrorGuaranteed { + fn visit_with>>(&self, visitor: &mut V) -> V::Result { + visitor.visit_error(*self) + } +} + +impl<'tcx> TypeFoldable> for rustc_span::ErrorGuaranteed { + fn try_fold_with>>( + self, + _folder: &mut F, + ) -> Result { + Ok(self) + } +} + impl<'tcx> TypeFoldable> for InferConst { fn try_fold_with>>( self, @@ -617,12 +632,6 @@ impl<'tcx> TypeVisitable> for InferConst { } } -impl<'tcx> TypeSuperVisitable> for ty::UnevaluatedConst<'tcx> { - fn super_visit_with>>(&self, visitor: &mut V) -> V::Result { - self.args.visit_with(visitor) - } -} - impl<'tcx> TypeVisitable> for TyAndLayout<'tcx, Ty<'tcx>> { fn visit_with>>(&self, visitor: &mut V) -> V::Result { visitor.visit_ty(self.ty) diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 24e3e623ff274..a6dec66449e9f 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -607,7 +607,9 @@ impl<'a, V> ::std::ops::Index for LocalTableInContext<'a, V> { type Output = V; fn index(&self, key: HirId) -> &V { - self.get(key).expect("LocalTableInContext: key not found") + self.get(key).unwrap_or_else(|| { + bug!("LocalTableInContext({:?}): key {:?} not found", self.hir_owner, key) + }) } } diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index 5d828d0093f3c..529e9cc271136 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start +either = "1.5.0" itertools = "0.12" rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 0c277811fdacf..281f3ef6ef35a 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -4,8 +4,6 @@ mir_build_already_borrowed = cannot borrow value as mutable because it is also b mir_build_already_mut_borrowed = cannot borrow value as immutable because it is also borrowed as mutable -mir_build_assoc_const_in_pattern = associated consts cannot be referenced in patterns - mir_build_bindings_with_variant_name = pattern binding `{$name}` is named the same as one of the variants of the type `{$ty_path}` .suggestion = to match on the variant, qualify the path diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index 5ccbd7c59cfba..c608e5c63d842 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -71,11 +71,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { StmtKind::Expr { scope, expr } => { this.block_context.push(BlockFrame::Statement { ignores_expr_result: true }); let si = (*scope, source_info); - unpack!( - block = this.in_scope(si, LintLevel::Inherited, |this| { + block = this + .in_scope(si, LintLevel::Inherited, |this| { this.stmt_expr(block, *expr, Some(*scope)) }) - ); + .into_block(); } StmtKind::Let { remainder_scope, @@ -166,14 +166,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let dummy_place = this.temp(this.tcx.types.never, else_block_span); let failure_entry = this.cfg.start_new_block(); let failure_block; - unpack!( - failure_block = this.ast_block( + failure_block = this + .ast_block( dummy_place, failure_entry, *else_block, this.source_info(else_block_span), ) - ); + .into_block(); this.cfg.terminate( failure_block, this.source_info(else_block_span), @@ -267,8 +267,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let initializer_span = this.thir[init].span; let scope = (*init_scope, source_info); - unpack!( - block = this.in_scope(scope, *lint_level, |this| { + block = this + .in_scope(scope, *lint_level, |this| { this.declare_bindings( visibility_scope, remainder_span, @@ -279,10 +279,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.expr_into_pattern(block, &pattern, init) // irrefutable pattern }) - ) + .into_block(); } else { let scope = (*init_scope, source_info); - unpack!(this.in_scope(scope, *lint_level, |this| { + let _: BlockAnd<()> = this.in_scope(scope, *lint_level, |this| { this.declare_bindings( visibility_scope, remainder_span, @@ -291,7 +291,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None, ); block.unit() - })); + }); debug!("ast_block_stmts: pattern={:?}", pattern); this.visit_primary_bindings( @@ -333,7 +333,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.block_context .push(BlockFrame::TailExpr { tail_result_is_ignored, span: expr.span }); - unpack!(block = this.expr_into_dest(destination, block, expr_id)); + block = this.expr_into_dest(destination, block, expr_id).into_block(); let popped = this.block_context.pop(); assert!(popped.is_some_and(|bf| bf.is_tail_expr())); @@ -355,7 +355,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Finally, we pop all the let scopes before exiting out from the scope of block // itself. for scope in let_scope_stack.into_iter().rev() { - unpack!(block = this.pop_scope((*scope, source_info), block)); + block = this.pop_scope((*scope, source_info), block).into_block(); } // Restore the original source scope. this.source_scope = outer_source_scope; diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index c5ee6db5999a5..40cfe563accea 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -185,13 +185,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.cfg.push_assign(block, source_info, Place::from(result), box_); // initialize the box contents: - unpack!( - block = this.expr_into_dest( - this.tcx.mk_place_deref(Place::from(result)), - block, - value, - ) - ); + block = this + .expr_into_dest(this.tcx.mk_place_deref(Place::from(result)), block, value) + .into_block(); block.and(Rvalue::Use(Operand::Move(Place::from(result)))) } ExprKind::Cast { source } => { @@ -486,7 +482,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(Rvalue::Aggregate(result, operands)) } ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { - block = unpack!(this.stmt_expr(block, expr_id, None)); + block = this.stmt_expr(block, expr_id, None).into_block(); block.and(Rvalue::Use(Operand::Constant(Box::new(ConstOperand { span: expr_span, user_ty: None, diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs index 607c7c3259c18..82673582e796d 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs @@ -112,7 +112,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - unpack!(block = this.expr_into_dest(temp_place, block, expr_id)); + block = this.expr_into_dest(temp_place, block, expr_id).into_block(); if let Some(temp_lifetime) = temp_lifetime { this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value); diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 942c69b5c0a75..9cd958a21da4d 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -82,13 +82,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Lower the condition, and have it branch into `then` and `else` blocks. let (then_block, else_block) = this.in_if_then_scope(condition_scope, then_span, |this| { - let then_blk = unpack!(this.then_else_break( - block, - cond, - Some(condition_scope), // Temp scope - source_info, - DeclareLetBindings::Yes, // Declare `let` bindings normally - )); + let then_blk = this + .then_else_break( + block, + cond, + Some(condition_scope), // Temp scope + source_info, + DeclareLetBindings::Yes, // Declare `let` bindings normally + ) + .into_block(); // Lower the `then` arm into its block. this.expr_into_dest(destination, then_blk, then) @@ -105,7 +107,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // If there is an `else` arm, lower it into `else_blk`. if let Some(else_expr) = else_opt { - unpack!(else_blk = this.expr_into_dest(destination, else_blk, else_expr)); + else_blk = this.expr_into_dest(destination, else_blk, else_expr).into_block(); } else { // There is no `else` arm, so we know both arms have type `()`. // Generate the implicit `else {}` by assigning unit. @@ -187,7 +189,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { const_: Const::from_bool(this.tcx, constant), }, ); - let mut rhs_block = unpack!(this.expr_into_dest(destination, continuation, rhs)); + let mut rhs_block = + this.expr_into_dest(destination, continuation, rhs).into_block(); // Instrument the lowered RHS's value for condition coverage. // (Does nothing if condition coverage is not enabled.) this.visit_coverage_standalone_condition(rhs, destination, &mut rhs_block); @@ -230,7 +233,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // introduce a unit temporary as the destination for the loop body. let tmp = this.get_unit_temp(); // Execute the body, branching back to the test. - let body_block_end = unpack!(this.expr_into_dest(tmp, body_block, body)); + let body_block_end = this.expr_into_dest(tmp, body_block, body).into_block(); this.cfg.goto(body_block_end, source_info, loop_block); // Loops are only exited by `break` expressions. @@ -462,7 +465,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { targets.push(target); let tmp = this.get_unit_temp(); - let target = unpack!(this.ast_block(tmp, target, block, source_info)); + let target = + this.ast_block(tmp, target, block, source_info).into_block(); this.cfg.terminate( target, source_info, @@ -502,7 +506,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // These cases don't actually need a destination ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { - unpack!(block = this.stmt_expr(block, expr_id, None)); + block = this.stmt_expr(block, expr_id, None).into_block(); this.cfg.push_assign_unit(block, source_info, destination, this.tcx); block.unit() } @@ -511,7 +515,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::Break { .. } | ExprKind::Return { .. } | ExprKind::Become { .. } => { - unpack!(block = this.stmt_expr(block, expr_id, None)); + block = this.stmt_expr(block, expr_id, None).into_block(); // No assign, as these have type `!`. block.unit() } diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs index 88b76c46c90bf..8e13edb4c89b7 100644 --- a/compiler/rustc_mir_build/src/build/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs @@ -44,7 +44,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if lhs_expr.ty.needs_drop(this.tcx, this.param_env) { let rhs = unpack!(block = this.as_local_rvalue(block, rhs)); let lhs = unpack!(block = this.as_place(block, lhs)); - unpack!(block = this.build_drop_and_replace(block, lhs_expr.span, lhs, rhs)); + block = + this.build_drop_and_replace(block, lhs_expr.span, lhs, rhs).into_block(); } else { let rhs = unpack!(block = this.as_local_rvalue(block, rhs)); let lhs = unpack!(block = this.as_place(block, lhs)); diff --git a/compiler/rustc_mir_build/src/build/matches/match_pair.rs b/compiler/rustc_mir_build/src/build/matches/match_pair.rs new file mode 100644 index 0000000000000..95fec154918a5 --- /dev/null +++ b/compiler/rustc_mir_build/src/build/matches/match_pair.rs @@ -0,0 +1,258 @@ +use rustc_middle::mir::*; +use rustc_middle::thir::{self, *}; +use rustc_middle::ty::{self, Ty, TypeVisitableExt}; + +use crate::build::expr::as_place::{PlaceBase, PlaceBuilder}; +use crate::build::matches::{FlatPat, MatchPairTree, TestCase}; +use crate::build::Builder; + +impl<'a, 'tcx> Builder<'a, 'tcx> { + /// Builds and returns [`MatchPairTree`] subtrees, one for each pattern in + /// `subpatterns`, representing the fields of a [`PatKind::Variant`] or + /// [`PatKind::Leaf`]. + /// + /// Used internally by [`MatchPairTree::for_pattern`]. + fn field_match_pairs<'pat>( + &mut self, + place: PlaceBuilder<'tcx>, + subpatterns: &'pat [FieldPat<'tcx>], + ) -> Vec> { + subpatterns + .iter() + .map(|fieldpat| { + let place = + place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty)); + MatchPairTree::for_pattern(place, &fieldpat.pattern, self) + }) + .collect() + } + + /// Builds [`MatchPairTree`] subtrees for the prefix/middle/suffix parts of an + /// array pattern or slice pattern, and adds those trees to `match_pairs`. + /// + /// Used internally by [`MatchPairTree::for_pattern`]. + fn prefix_slice_suffix<'pat>( + &mut self, + match_pairs: &mut Vec>, + place: &PlaceBuilder<'tcx>, + prefix: &'pat [Box>], + opt_slice: &'pat Option>>, + suffix: &'pat [Box>], + ) { + let tcx = self.tcx; + let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) { + match place_resolved.ty(&self.local_decls, tcx).ty.kind() { + ty::Array(_, length) => (length.eval_target_usize(tcx, self.param_env), true), + _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false), + } + } else { + ((prefix.len() + suffix.len()).try_into().unwrap(), false) + }; + + match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| { + let elem = + ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false }; + MatchPairTree::for_pattern(place.clone_project(elem), subpattern, self) + })); + + if let Some(subslice_pat) = opt_slice { + let suffix_len = suffix.len() as u64; + let subslice = place.clone_project(PlaceElem::Subslice { + from: prefix.len() as u64, + to: if exact_size { min_length - suffix_len } else { suffix_len }, + from_end: !exact_size, + }); + match_pairs.push(MatchPairTree::for_pattern(subslice, subslice_pat, self)); + } + + match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| { + let end_offset = (idx + 1) as u64; + let elem = ProjectionElem::ConstantIndex { + offset: if exact_size { min_length - end_offset } else { end_offset }, + min_length, + from_end: !exact_size, + }; + let place = place.clone_project(elem); + MatchPairTree::for_pattern(place, subpattern, self) + })); + } +} + +impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { + /// Recursively builds a match pair tree for the given pattern and its + /// subpatterns. + pub(in crate::build) fn for_pattern( + mut place_builder: PlaceBuilder<'tcx>, + pattern: &'pat Pat<'tcx>, + cx: &mut Builder<'_, 'tcx>, + ) -> MatchPairTree<'pat, 'tcx> { + // Force the place type to the pattern's type. + // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks? + if let Some(resolved) = place_builder.resolve_upvar(cx) { + place_builder = resolved; + } + + // Only add the OpaqueCast projection if the given place is an opaque type and the + // expected type from the pattern is not. + let may_need_cast = match place_builder.base() { + PlaceBase::Local(local) => { + let ty = + Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx).ty; + ty != pattern.ty && ty.has_opaque_types() + } + _ => true, + }; + if may_need_cast { + place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty)); + } + + let place = place_builder.try_to_place(cx); + let default_irrefutable = || TestCase::Irrefutable { binding: None, ascription: None }; + let mut subpairs = Vec::new(); + let test_case = match pattern.kind { + PatKind::Wild | PatKind::Error(_) => default_irrefutable(), + + PatKind::Or { ref pats } => TestCase::Or { + pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(), + }, + + PatKind::Range(ref range) => { + if range.is_full_range(cx.tcx) == Some(true) { + default_irrefutable() + } else { + TestCase::Range(range) + } + } + + PatKind::Constant { value } => TestCase::Constant { value }, + + PatKind::AscribeUserType { + ascription: thir::Ascription { ref annotation, variance }, + ref subpattern, + .. + } => { + // Apply the type ascription to the value at `match_pair.place` + let ascription = place.map(|source| super::Ascription { + annotation: annotation.clone(), + source, + variance, + }); + + subpairs.push(MatchPairTree::for_pattern(place_builder, subpattern, cx)); + TestCase::Irrefutable { ascription, binding: None } + } + + PatKind::Binding { mode, var, ref subpattern, .. } => { + let binding = place.map(|source| super::Binding { + span: pattern.span, + source, + var_id: var, + binding_mode: mode, + }); + + if let Some(subpattern) = subpattern.as_ref() { + // this is the `x @ P` case; have to keep matching against `P` now + subpairs.push(MatchPairTree::for_pattern(place_builder, subpattern, cx)); + } + TestCase::Irrefutable { ascription: None, binding } + } + + PatKind::InlineConstant { subpattern: ref pattern, def, .. } => { + // Apply a type ascription for the inline constant to the value at `match_pair.place` + let ascription = place.map(|source| { + let span = pattern.span; + let parent_id = cx.tcx.typeck_root_def_id(cx.def_id.to_def_id()); + let args = ty::InlineConstArgs::new( + cx.tcx, + ty::InlineConstArgsParts { + parent_args: ty::GenericArgs::identity_for_item(cx.tcx, parent_id), + ty: cx.infcx.next_ty_var(span), + }, + ) + .args; + let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::TypeOf( + def.to_def_id(), + ty::UserArgs { args, user_self_ty: None }, + )); + let annotation = ty::CanonicalUserTypeAnnotation { + inferred_ty: pattern.ty, + span, + user_ty: Box::new(user_ty), + }; + super::Ascription { annotation, source, variance: ty::Contravariant } + }); + + subpairs.push(MatchPairTree::for_pattern(place_builder, pattern, cx)); + TestCase::Irrefutable { ascription, binding: None } + } + + PatKind::Array { ref prefix, ref slice, ref suffix } => { + cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix); + default_irrefutable() + } + PatKind::Slice { ref prefix, ref slice, ref suffix } => { + cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix); + + if prefix.is_empty() && slice.is_some() && suffix.is_empty() { + default_irrefutable() + } else { + TestCase::Slice { + len: prefix.len() + suffix.len(), + variable_length: slice.is_some(), + } + } + } + + PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => { + let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)` + subpairs = cx.field_match_pairs(downcast_place, subpatterns); + + let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| { + i == variant_index || { + (cx.tcx.features().exhaustive_patterns + || cx.tcx.features().min_exhaustive_patterns) + && !v + .inhabited_predicate(cx.tcx, adt_def) + .instantiate(cx.tcx, args) + .apply_ignore_module(cx.tcx, cx.param_env) + } + }) && (adt_def.did().is_local() + || !adt_def.is_variant_list_non_exhaustive()); + if irrefutable { + default_irrefutable() + } else { + TestCase::Variant { adt_def, variant_index } + } + } + + PatKind::Leaf { ref subpatterns } => { + subpairs = cx.field_match_pairs(place_builder, subpatterns); + default_irrefutable() + } + + PatKind::Deref { ref subpattern } => { + subpairs.push(MatchPairTree::for_pattern(place_builder.deref(), subpattern, cx)); + default_irrefutable() + } + + PatKind::DerefPattern { ref subpattern, mutability } => { + // Create a new temporary for each deref pattern. + // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls? + let temp = cx.temp( + Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability), + pattern.span, + ); + subpairs.push(MatchPairTree::for_pattern( + PlaceBuilder::from(temp).deref(), + subpattern, + cx, + )); + TestCase::Deref { temp, mutability } + } + + PatKind::Never => TestCase::Never, + }; + + MatchPairTree { place, test_case, subpairs, pattern } + } +} diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 841ef2719c99d..b531a392efac9 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -24,6 +24,7 @@ use tracing::{debug, instrument}; use util::visit_bindings; // helper functions, broken out by category: +mod match_pair; mod simplify; mod test; mod util; @@ -121,8 +122,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match expr.kind { ExprKind::LogicalOp { op: op @ LogicalOp::And, lhs, rhs } => { this.visit_coverage_branch_operation(op, expr_span); - let lhs_then_block = unpack!(this.then_else_break_inner(block, lhs, args)); - let rhs_then_block = unpack!(this.then_else_break_inner(lhs_then_block, rhs, args)); + let lhs_then_block = this.then_else_break_inner(block, lhs, args).into_block(); + let rhs_then_block = + this.then_else_break_inner(lhs_then_block, rhs, args).into_block(); rhs_then_block.unit() } ExprKind::LogicalOp { op: op @ LogicalOp::Or, lhs, rhs } => { @@ -139,14 +141,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }, ) }); - let rhs_success_block = unpack!(this.then_else_break_inner( - failure_block, - rhs, - ThenElseArgs { - declare_let_bindings: DeclareLetBindings::LetNotPermitted, - ..args - }, - )); + let rhs_success_block = this + .then_else_break_inner( + failure_block, + rhs, + ThenElseArgs { + declare_let_bindings: DeclareLetBindings::LetNotPermitted, + ..args + }, + ) + .into_block(); // Make the LHS and RHS success arms converge to a common block. // (We can't just make LHS goto RHS, because `rhs_success_block` @@ -451,7 +455,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { outer_source_info: SourceInfo, fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)>, ) -> BlockAnd<()> { - let arm_end_blocks: Vec<_> = arm_candidates + let arm_end_blocks: Vec = arm_candidates .into_iter() .map(|(arm, candidate)| { debug!("lowering arm {:?}\ncandidate = {:?}", arm, candidate); @@ -502,6 +506,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.expr_into_dest(destination, arm_block, arm.body) }) + .into_block() }) .collect(); @@ -512,10 +517,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { outer_source_info.span.with_lo(outer_source_info.span.hi() - BytePos::from_usize(1)), ); for arm_block in arm_end_blocks { - let block = &self.cfg.basic_blocks[arm_block.0]; + let block = &self.cfg.basic_blocks[arm_block]; let last_location = block.statements.last().map(|s| s.source_info); - self.cfg.goto(unpack!(arm_block), last_location.unwrap_or(end_brace), end_block); + self.cfg.goto(arm_block, last_location.unwrap_or(end_brace), end_block); } self.source_scope = outer_source_info.scope; @@ -621,7 +626,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { OutsideGuard, ScheduleDrops::Yes, ); - unpack!(block = self.expr_into_dest(place, block, initializer_id)); + block = self.expr_into_dest(place, block, initializer_id).into_block(); // Inject a fake read, see comments on `FakeReadCause::ForLet`. let source_info = self.source_info(irrefutable_pat.span); @@ -660,7 +665,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { OutsideGuard, ScheduleDrops::Yes, ); - unpack!(block = self.expr_into_dest(place, block, initializer_id)); + block = self.expr_into_dest(place, block, initializer_id).into_block(); // Inject a fake read, see comments on `FakeReadCause::ForLet`. let pattern_source_info = self.source_info(irrefutable_pat.span); @@ -1027,15 +1032,15 @@ impl<'tcx> PatternExtraData<'tcx> { #[derive(Debug, Clone)] struct FlatPat<'pat, 'tcx> { /// To match the pattern, all of these must be satisfied... - // Invariant: all the `MatchPair`s are recursively simplified. + // Invariant: all the match pairs are recursively simplified. // Invariant: or-patterns must be sorted to the end. - match_pairs: Vec>, + match_pairs: Vec>, extra_data: PatternExtraData<'tcx>, } impl<'tcx, 'pat> FlatPat<'pat, 'tcx> { - /// Creates a `FlatPat` containing a simplified [`MatchPair`] list/forest + /// Creates a `FlatPat` containing a simplified [`MatchPairTree`] list/forest /// for the given pattern. fn new( place: PlaceBuilder<'tcx>, @@ -1043,7 +1048,7 @@ impl<'tcx, 'pat> FlatPat<'pat, 'tcx> { cx: &mut Builder<'_, 'tcx>, ) -> Self { // First, recursively build a tree of match pairs for the given pattern. - let mut match_pairs = vec![MatchPair::new(place, pattern, cx)]; + let mut match_pairs = vec![MatchPairTree::for_pattern(place, pattern, cx)]; let mut extra_data = PatternExtraData { span: pattern.span, bindings: Vec::new(), @@ -1060,9 +1065,9 @@ impl<'tcx, 'pat> FlatPat<'pat, 'tcx> { #[derive(Debug)] struct Candidate<'pat, 'tcx> { /// For the candidate to match, all of these must be satisfied... - // Invariant: all the `MatchPair`s are recursively simplified. + // Invariant: all the match pairs are recursively simplified. // Invariant: or-patterns must be sorted at the end. - match_pairs: Vec>, + match_pairs: Vec>, /// ...and if this is non-empty, one of these subcandidates also has to match... // Invariant: at the end of the algorithm, this must never contain a `is_never` candidate @@ -1121,7 +1126,7 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { /// Returns whether the first match pair of this candidate is an or-pattern. fn starts_with_or_pattern(&self) -> bool { - matches!(&*self.match_pairs, [MatchPair { test_case: TestCase::Or { .. }, .. }, ..]) + matches!(&*self.match_pairs, [MatchPairTree { test_case: TestCase::Or { .. }, .. }, ..]) } /// Visit the leaf candidates (those with no subcandidates) contained in @@ -1195,17 +1200,27 @@ impl<'pat, 'tcx> TestCase<'pat, 'tcx> { } } +/// Node in a tree of "match pairs", where each pair consists of a place to be +/// tested, and a test to perform on that place. +/// +/// Each node also has a list of subpairs (possibly empty) that must also match, +/// and a reference to the THIR pattern it represents. #[derive(Debug, Clone)] -pub(crate) struct MatchPair<'pat, 'tcx> { +pub(crate) struct MatchPairTree<'pat, 'tcx> { /// This place... - // This can be `None` if it referred to a non-captured place in a closure. - // Invariant: place.is_none() => test_case is Irrefutable - // In other words this must be `Some(_)` after simplification. + /// + /// --- + /// This can be `None` if it referred to a non-captured place in a closure. + /// + /// Invariant: Can only be `None` when `test_case` is `Irrefutable`. + /// Therefore this must be `Some(_)` after simplification. place: Option>, /// ... must pass this test... - // Invariant: after creation and simplification in `Candidate::new()`, this must not be - // `Irrefutable`. + /// + /// --- + /// Invariant: after creation and simplification in [`FlatPat::new`], + /// this must not be [`TestCase::Irrefutable`]. test_case: TestCase<'pat, 'tcx>, /// ... and these subpairs must match. @@ -1536,10 +1551,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { start_block: BasicBlock, candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], ) -> BlockAnd<&'b mut [&'c mut Candidate<'pat, 'tcx>]> { - // We can't expand or-patterns freely. The rule is: if the candidate has an - // or-pattern as its only remaining match pair, we can expand it freely. If it has - // other match pairs, we can expand it but we can't process more candidates after - // it. + // We can't expand or-patterns freely. The rule is: + // - If a candidate doesn't start with an or-pattern, we include it in + // the expansion list as-is (i.e. it "expands" to itself). + // - If a candidate has an or-pattern as its only remaining match pair, + // we can expand it. + // - If it starts with an or-pattern but also has other match pairs, + // we can expand it, but we can't process more candidates after it. // // If we didn't stop, the `otherwise` cases could get mixed up. E.g. in the // following, or-pattern simplification (in `merge_trivial_subcandidates`) makes it @@ -1556,17 +1574,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // } // ``` // - // We therefore split the `candidates` slice in two, expand or-patterns in the first half, + // We therefore split the `candidates` slice in two, expand or-patterns in the first part, // and process the rest separately. - let mut expand_until = 0; - for (i, candidate) in candidates.iter().enumerate() { - expand_until = i + 1; - if candidate.match_pairs.len() > 1 && candidate.starts_with_or_pattern() { - // The candidate has an or-pattern as well as more match pairs: we must - // split the candidates list here. - break; - } - } + let expand_until = candidates + .iter() + .position(|candidate| { + // If a candidate starts with an or-pattern and has more match pairs, + // we can expand it, but we must stop expanding _after_ it. + candidate.match_pairs.len() > 1 && candidate.starts_with_or_pattern() + }) + .map(|pos| pos + 1) // Stop _after_ the found candidate + .unwrap_or(candidates.len()); // Otherwise, include all candidates let (candidates_to_expand, remaining_candidates) = candidates.split_at_mut(expand_until); // Expand one level of or-patterns for each candidate in `candidates_to_expand`. @@ -1581,6 +1599,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expanded_candidates.push(subcandidate); } } else { + // A candidate that doesn't start with an or-pattern has nothing to + // expand, so it is included in the post-expansion list as-is. expanded_candidates.push(candidate); } } @@ -1609,7 +1629,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn create_or_subcandidates<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, - match_pair: MatchPair<'pat, 'tcx>, + match_pair: MatchPairTree<'pat, 'tcx>, ) { let TestCase::Or { pats } = match_pair.test_case else { bug!() }; debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats); @@ -1793,8 +1813,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// [`Range`]: TestKind::Range fn pick_test(&mut self, candidates: &[&mut Candidate<'_, 'tcx>]) -> (Place<'tcx>, Test<'tcx>) { // Extract the match-pair from the highest priority candidate - let match_pair = &candidates.first().unwrap().match_pairs[0]; - let test = self.test(match_pair); + let match_pair = &candidates[0].match_pairs[0]; + let test = self.pick_test_for_match_pair(match_pair); // Unwrap is ok after simplification. let match_place = match_pair.place.unwrap(); debug!(?test, ?match_pair); diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index 543301c71a0ec..20310f6082108 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -12,7 +12,7 @@ //! sort of test: for example, testing which variant an enum is, or //! testing a value against a constant. -use crate::build::matches::{MatchPair, PatternExtraData, TestCase}; +use crate::build::matches::{MatchPairTree, PatternExtraData, TestCase}; use crate::build::Builder; use tracing::{debug, instrument}; @@ -24,7 +24,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { #[instrument(skip(self), level = "debug")] pub(super) fn simplify_match_pairs<'pat>( &mut self, - match_pairs: &mut Vec>, + match_pairs: &mut Vec>, extra_data: &mut PatternExtraData<'tcx>, ) { // In order to please the borrow checker, in a pattern like `x @ pat` we must lower the diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index d29874a5ad4ba..8a02ea1a06de4 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -5,7 +5,7 @@ // identify what tests are needed, perform the tests, and then filter // the candidates based on the result. -use crate::build::matches::{Candidate, MatchPair, Test, TestBranch, TestCase, TestKind}; +use crate::build::matches::{Candidate, MatchPairTree, Test, TestBranch, TestCase, TestKind}; use crate::build::Builder; use rustc_data_structures::fx::FxIndexMap; use rustc_hir::{LangItem, RangeEnd}; @@ -26,7 +26,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Identifies what test is needed to decide if `match_pair` is applicable. /// /// It is a bug to call this with a not-fully-simplified pattern. - pub(super) fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> { + pub(super) fn pick_test_for_match_pair<'pat>( + &mut self, + match_pair: &MatchPairTree<'pat, 'tcx>, + ) -> Test<'tcx> { let kind = match match_pair.test_case { TestCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def }, @@ -144,7 +147,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { && tcx.is_lang_item(def.did(), LangItem::String) { if !tcx.features().string_deref_patterns { - bug!( + span_bug!( + test.span, "matching on `String` went through without enabling string_deref_patterns" ); } @@ -432,40 +436,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - match *ty.kind() { - ty::Ref(_, deref_ty, _) => ty = deref_ty, - _ => { - // non_scalar_compare called on non-reference type - let temp = self.temp(ty, source_info.span); - self.cfg.push_assign(block, source_info, temp, Rvalue::Use(expect)); - let ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, ty); - let ref_temp = self.temp(ref_ty, source_info.span); - - self.cfg.push_assign( - block, - source_info, - ref_temp, - Rvalue::Ref(self.tcx.lifetimes.re_erased, BorrowKind::Shared, temp), - ); - expect = Operand::Move(ref_temp); - - let ref_temp = self.temp(ref_ty, source_info.span); - self.cfg.push_assign( - block, - source_info, - ref_temp, - Rvalue::Ref(self.tcx.lifetimes.re_erased, BorrowKind::Shared, val), - ); - val = ref_temp; + // Figure out the type on which we are calling `PartialEq`. This involves an extra wrapping + // reference: we can only compare two `&T`, and then compare_ty will be `T`. + // Make sure that we do *not* call any user-defined code here. + // The only types that can end up here are string and byte literals, + // which have their comparison defined in `core`. + // (Interestingly this means that exhaustiveness analysis relies, for soundness, + // on the `PartialEq` impls for `str` and `[u8]` to b correct!) + let compare_ty = match *ty.kind() { + ty::Ref(_, deref_ty, _) + if deref_ty == self.tcx.types.str_ || deref_ty != self.tcx.types.u8 => + { + deref_ty } - } + _ => span_bug!(source_info.span, "invalid type for non-scalar compare: {}", ty), + }; let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, Some(source_info.span)); let method = trait_method( self.tcx, eq_def_id, sym::eq, - self.tcx.with_opt_host_effect_param(self.def_id, eq_def_id, [ty, ty]), + self.tcx.with_opt_host_effect_param(self.def_id, eq_def_id, [compare_ty, compare_ty]), ); let bool_ty = self.tcx.types.bool; diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index 3bec154e1df5d..8fe8069b3455d 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -1,78 +1,15 @@ use std::marker::PhantomData; -use crate::build::expr::as_place::{PlaceBase, PlaceBuilder}; -use crate::build::matches::{Binding, Candidate, FlatPat, MatchPair, TestCase}; +use crate::build::expr::as_place::PlaceBase; +use crate::build::matches::{Binding, Candidate, FlatPat, MatchPairTree, TestCase}; use crate::build::Builder; use rustc_data_structures::fx::FxIndexMap; use rustc_middle::mir::*; -use rustc_middle::thir::{self, *}; -use rustc_middle::ty::TypeVisitableExt; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::Ty; use rustc_span::Span; use tracing::debug; impl<'a, 'tcx> Builder<'a, 'tcx> { - pub(crate) fn field_match_pairs<'pat>( - &mut self, - place: PlaceBuilder<'tcx>, - subpatterns: &'pat [FieldPat<'tcx>], - ) -> Vec> { - subpatterns - .iter() - .map(|fieldpat| { - let place = - place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty)); - MatchPair::new(place, &fieldpat.pattern, self) - }) - .collect() - } - - pub(crate) fn prefix_slice_suffix<'pat>( - &mut self, - match_pairs: &mut Vec>, - place: &PlaceBuilder<'tcx>, - prefix: &'pat [Box>], - opt_slice: &'pat Option>>, - suffix: &'pat [Box>], - ) { - let tcx = self.tcx; - let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) { - match place_resolved.ty(&self.local_decls, tcx).ty.kind() { - ty::Array(_, length) => (length.eval_target_usize(tcx, self.param_env), true), - _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false), - } - } else { - ((prefix.len() + suffix.len()).try_into().unwrap(), false) - }; - - match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| { - let elem = - ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false }; - MatchPair::new(place.clone_project(elem), subpattern, self) - })); - - if let Some(subslice_pat) = opt_slice { - let suffix_len = suffix.len() as u64; - let subslice = place.clone_project(PlaceElem::Subslice { - from: prefix.len() as u64, - to: if exact_size { min_length - suffix_len } else { suffix_len }, - from_end: !exact_size, - }); - match_pairs.push(MatchPair::new(subslice, subslice_pat, self)); - } - - match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| { - let end_offset = (idx + 1) as u64; - let elem = ProjectionElem::ConstantIndex { - offset: if exact_size { min_length - end_offset } else { end_offset }, - min_length, - from_end: !exact_size, - }; - let place = place.clone_project(elem); - MatchPair::new(place, subpattern, self) - })); - } - /// Creates a false edge to `imaginary_target` and a real edge to /// real_target. If `imaginary_target` is none, or is the same as the real /// target, a Goto is generated instead to simplify the generated MIR. @@ -96,181 +33,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } -impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { - /// Recursively builds a `MatchPair` tree for the given pattern and its - /// subpatterns. - pub(in crate::build) fn new( - mut place_builder: PlaceBuilder<'tcx>, - pattern: &'pat Pat<'tcx>, - cx: &mut Builder<'_, 'tcx>, - ) -> MatchPair<'pat, 'tcx> { - // Force the place type to the pattern's type. - // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks? - if let Some(resolved) = place_builder.resolve_upvar(cx) { - place_builder = resolved; - } - - // Only add the OpaqueCast projection if the given place is an opaque type and the - // expected type from the pattern is not. - let may_need_cast = match place_builder.base() { - PlaceBase::Local(local) => { - let ty = - Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx).ty; - ty != pattern.ty && ty.has_opaque_types() - } - _ => true, - }; - if may_need_cast { - place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty)); - } - - let place = place_builder.try_to_place(cx); - let default_irrefutable = || TestCase::Irrefutable { binding: None, ascription: None }; - let mut subpairs = Vec::new(); - let test_case = match pattern.kind { - PatKind::Wild | PatKind::Error(_) => default_irrefutable(), - - PatKind::Or { ref pats } => TestCase::Or { - pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(), - }, - - PatKind::Range(ref range) => { - if range.is_full_range(cx.tcx) == Some(true) { - default_irrefutable() - } else { - TestCase::Range(range) - } - } - - PatKind::Constant { value } => TestCase::Constant { value }, - - PatKind::AscribeUserType { - ascription: thir::Ascription { ref annotation, variance }, - ref subpattern, - .. - } => { - // Apply the type ascription to the value at `match_pair.place` - let ascription = place.map(|source| super::Ascription { - annotation: annotation.clone(), - source, - variance, - }); - - subpairs.push(MatchPair::new(place_builder, subpattern, cx)); - TestCase::Irrefutable { ascription, binding: None } - } - - PatKind::Binding { mode, var, ref subpattern, .. } => { - let binding = place.map(|source| super::Binding { - span: pattern.span, - source, - var_id: var, - binding_mode: mode, - }); - - if let Some(subpattern) = subpattern.as_ref() { - // this is the `x @ P` case; have to keep matching against `P` now - subpairs.push(MatchPair::new(place_builder, subpattern, cx)); - } - TestCase::Irrefutable { ascription: None, binding } - } - - PatKind::InlineConstant { subpattern: ref pattern, def, .. } => { - // Apply a type ascription for the inline constant to the value at `match_pair.place` - let ascription = place.map(|source| { - let span = pattern.span; - let parent_id = cx.tcx.typeck_root_def_id(cx.def_id.to_def_id()); - let args = ty::InlineConstArgs::new( - cx.tcx, - ty::InlineConstArgsParts { - parent_args: ty::GenericArgs::identity_for_item(cx.tcx, parent_id), - ty: cx.infcx.next_ty_var(span), - }, - ) - .args; - let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::TypeOf( - def.to_def_id(), - ty::UserArgs { args, user_self_ty: None }, - )); - let annotation = ty::CanonicalUserTypeAnnotation { - inferred_ty: pattern.ty, - span, - user_ty: Box::new(user_ty), - }; - super::Ascription { annotation, source, variance: ty::Contravariant } - }); - - subpairs.push(MatchPair::new(place_builder, pattern, cx)); - TestCase::Irrefutable { ascription, binding: None } - } - - PatKind::Array { ref prefix, ref slice, ref suffix } => { - cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix); - default_irrefutable() - } - PatKind::Slice { ref prefix, ref slice, ref suffix } => { - cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix); - - if prefix.is_empty() && slice.is_some() && suffix.is_empty() { - default_irrefutable() - } else { - TestCase::Slice { - len: prefix.len() + suffix.len(), - variable_length: slice.is_some(), - } - } - } - - PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => { - let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)` - subpairs = cx.field_match_pairs(downcast_place, subpatterns); - - let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| { - i == variant_index || { - (cx.tcx.features().exhaustive_patterns - || cx.tcx.features().min_exhaustive_patterns) - && !v - .inhabited_predicate(cx.tcx, adt_def) - .instantiate(cx.tcx, args) - .apply_ignore_module(cx.tcx, cx.param_env) - } - }) && (adt_def.did().is_local() - || !adt_def.is_variant_list_non_exhaustive()); - if irrefutable { - default_irrefutable() - } else { - TestCase::Variant { adt_def, variant_index } - } - } - - PatKind::Leaf { ref subpatterns } => { - subpairs = cx.field_match_pairs(place_builder, subpatterns); - default_irrefutable() - } - - PatKind::Deref { ref subpattern } => { - subpairs.push(MatchPair::new(place_builder.deref(), subpattern, cx)); - default_irrefutable() - } - - PatKind::DerefPattern { ref subpattern, mutability } => { - // Create a new temporary for each deref pattern. - // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls? - let temp = cx.temp( - Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability), - pattern.span, - ); - subpairs.push(MatchPair::new(PlaceBuilder::from(temp).deref(), subpattern, cx)); - TestCase::Deref { temp, mutability } - } - - PatKind::Never => TestCase::Never, - }; - - MatchPair { place, test_case, subpairs, pattern } - } -} - /// Determine the set of places that have to be stable across match guards. /// /// Returns a list of places that need a fake borrow along with a local to store it. @@ -390,7 +152,7 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } } - fn visit_match_pair(&mut self, match_pair: &MatchPair<'_, 'tcx>) { + fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'_, 'tcx>) { if let TestCase::Or { pats, .. } = &match_pair.test_case { for flat_pat in pats.iter() { self.visit_flat_pat(flat_pat) @@ -498,7 +260,7 @@ where } } - fn visit_match_pair(&mut self, match_pair: &MatchPair<'_, 'tcx>) { + fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'_, 'tcx>) { if let TestCase::Or { pats, .. } = &match_pair.test_case { // All the or-alternatives should bind the same locals, so we only visit the first one. self.visit_flat_pat(&pats[0]) diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 0f9746cb719ca..2793a7d873621 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -403,6 +403,15 @@ enum NeedsTemporary { #[must_use = "if you don't use one of these results, you're leaving a dangling edge"] struct BlockAnd(BasicBlock, T); +impl BlockAnd<()> { + /// Unpacks `BlockAnd<()>` into a [`BasicBlock`]. + #[must_use] + fn into_block(self) -> BasicBlock { + let Self(block, ()) = self; + block + } +} + trait BlockAndExtension { fn and(self, v: T) -> BlockAnd; fn unit(self) -> BlockAnd<()>; @@ -426,11 +435,6 @@ macro_rules! unpack { $x = b; v }}; - - ($c:expr) => {{ - let BlockAnd(b, ()) = $c; - b - }}; } /////////////////////////////////////////////////////////////////////////// @@ -516,21 +520,22 @@ fn construct_fn<'tcx>( region::Scope { id: body.id().hir_id.local_id, data: region::ScopeData::Arguments }; let source_info = builder.source_info(span); let call_site_s = (call_site_scope, source_info); - unpack!(builder.in_scope(call_site_s, LintLevel::Inherited, |builder| { + let _: BlockAnd<()> = builder.in_scope(call_site_s, LintLevel::Inherited, |builder| { let arg_scope_s = (arg_scope, source_info); // Attribute epilogue to function's closing brace let fn_end = span_with_body.shrink_to_hi(); - let return_block = - unpack!(builder.in_breakable_scope(None, Place::return_place(), fn_end, |builder| { + let return_block = builder + .in_breakable_scope(None, Place::return_place(), fn_end, |builder| { Some(builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| { builder.args_and_body(START_BLOCK, arguments, arg_scope, expr) })) - })); + }) + .into_block(); let source_info = builder.source_info(fn_end); builder.cfg.terminate(return_block, source_info, TerminatorKind::Return); builder.build_drop_trees(); return_block.unit() - })); + }); let mut body = builder.finish(); @@ -579,7 +584,7 @@ fn construct_const<'a, 'tcx>( Builder::new(thir, infcx, def, hir_id, span, 0, const_ty, const_ty_span, None); let mut block = START_BLOCK; - unpack!(block = builder.expr_into_dest(Place::return_place(), block, expr)); + block = builder.expr_into_dest(Place::return_place(), block, expr).into_block(); let source_info = builder.source_info(span); builder.cfg.terminate(block, source_info, TerminatorKind::Return); @@ -961,7 +966,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Some((Some(&place), span)), ); let place_builder = PlaceBuilder::from(local); - unpack!(block = self.place_into_pattern(block, pat, place_builder, false)); + block = self.place_into_pattern(block, pat, place_builder, false).into_block(); } } self.source_scope = original_source_scope; diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 948301e2ece4d..b630c74a20283 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -510,12 +510,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let target = self.cfg.start_new_block(); let source_info = self.source_info(span); self.cfg.terminate( - unpack!(normal_block), + normal_block.into_block(), source_info, TerminatorKind::Goto { target }, ); self.cfg.terminate( - unpack!(exit_block), + exit_block.into_block(), source_info, TerminatorKind::Goto { target }, ); @@ -552,14 +552,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let scope = IfThenScope { region_scope, else_drops: DropTree::new() }; let previous_scope = mem::replace(&mut self.scopes.if_then_scope, Some(scope)); - let then_block = unpack!(f(self)); + let then_block = f(self).into_block(); let if_then_scope = mem::replace(&mut self.scopes.if_then_scope, previous_scope).unwrap(); assert!(if_then_scope.region_scope == region_scope); - let else_block = self - .build_exit_tree(if_then_scope.else_drops, region_scope, span, None) - .map_or_else(|| self.cfg.start_new_block(), |else_block_and| unpack!(else_block_and)); + let else_block = + self.build_exit_tree(if_then_scope.else_drops, region_scope, span, None).map_or_else( + || self.cfg.start_new_block(), + |else_block_and| else_block_and.into_block(), + ); (then_block, else_block) } @@ -585,7 +587,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.push_scope(region_scope); let mut block; let rv = unpack!(block = f(self)); - unpack!(block = self.pop_scope(region_scope, block)); + block = self.pop_scope(region_scope, block).into_block(); self.source_scope = source_scope; debug!(?block); block.and(rv) @@ -657,7 +659,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (Some(destination), Some(value)) => { debug!("stmt_expr Break val block_context.push(SubExpr)"); self.block_context.push(BlockFrame::SubExpr); - unpack!(block = self.expr_into_dest(destination, block, value)); + block = self.expr_into_dest(destination, block, value).into_block(); self.block_context.pop(); } (Some(destination), None) => { @@ -838,7 +840,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let unwind_to = if needs_cleanup { self.diverge_cleanup() } else { DropIdx::MAX }; let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes"); - unpack!(build_scope_drops( + build_scope_drops( &mut self.cfg, &mut self.scopes.unwind_drops, scope, @@ -846,7 +848,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { unwind_to, is_coroutine && needs_cleanup, self.arg_count, - )) + ) + .into_block() } /// Possibly creates a new source scope if `current_root` and `parent_root` diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 7c73d8a6d47d4..f6f443b64a63a 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -566,13 +566,6 @@ pub(crate) struct StaticInPattern { pub(crate) span: Span, } -#[derive(Diagnostic)] -#[diag(mir_build_assoc_const_in_pattern, code = E0158)] -pub(crate) struct AssocConstInPattern { - #[primary_span] - pub(crate) span: Span, -} - #[derive(Diagnostic)] #[diag(mir_build_const_param_in_pattern, code = E0158)] pub(crate) struct ConstParamInPattern { @@ -597,7 +590,7 @@ pub(crate) struct UnreachablePattern { } #[derive(Diagnostic)] -#[diag(mir_build_const_pattern_depends_on_generic_parameter)] +#[diag(mir_build_const_pattern_depends_on_generic_parameter, code = E0158)] pub(crate) struct ConstPatternDependsOnGenericParameter { #[primary_span] pub(crate) span: Span, diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 5745dc0969cce..0d54f332585aa 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,42 +1,45 @@ +use either::Either; use rustc_apfloat::Float; use rustc_hir as hir; use rustc_index::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::Obligation; use rustc_middle::mir; -use rustc_middle::span_bug; +use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::thir::{FieldPat, Pat, PatKind}; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt, ValTree}; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::Span; use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc_trait_selection::traits::{self, ObligationCause}; +use rustc_trait_selection::traits::ObligationCause; use tracing::{debug, instrument, trace}; -use std::cell::Cell; - use super::PatCtxt; use crate::errors::{ - InvalidPattern, NaNPattern, PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, - UnsizedPattern, + ConstPatternDependsOnGenericParameter, CouldNotEvalConstPattern, InvalidPattern, NaNPattern, + PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern, }; impl<'a, 'tcx> PatCtxt<'a, 'tcx> { - /// Converts an evaluated constant to a pattern (if possible). + /// Converts a constant to a pattern (if possible). /// This means aggregate values (like structs and enums) are converted /// to a pattern that matches the value (as if you'd compared via structural equality). /// - /// `cv` must be a valtree or a `mir::ConstValue`. + /// Only type system constants are supported, as we are using valtrees + /// as an intermediate step. Unfortunately those don't carry a type + /// so we have to carry one ourselves. #[instrument(level = "debug", skip(self), ret)] pub(super) fn const_to_pat( &self, - cv: mir::Const<'tcx>, + c: ty::Const<'tcx>, + ty: Ty<'tcx>, id: hir::HirId, span: Span, ) -> Box> { let infcx = self.tcx.infer_ctxt().build(); let mut convert = ConstToPat::new(self, id, span, infcx); - convert.to_pat(cv) + convert.to_pat(c, ty) } } @@ -45,23 +48,12 @@ struct ConstToPat<'tcx> { span: Span, param_env: ty::ParamEnv<'tcx>, - // This tracks if we emitted some hard error for a given const value, so that - // we will not subsequently issue an irrelevant lint for the same const - // value. - saw_const_match_error: Cell>, - // inference context used for checking `T: Structural` bounds. infcx: InferCtxt<'tcx>, treat_byte_string_as_slice: bool, } -/// This error type signals that we encountered a non-struct-eq situation. -/// We will fall back to calling `PartialEq::eq` on such patterns, -/// and exhaustiveness checking will consider them as matching nothing. -#[derive(Debug)] -struct FallbackToOpaqueConst; - impl<'tcx> ConstToPat<'tcx> { fn new( pat_ctxt: &PatCtxt<'_, 'tcx>, @@ -75,7 +67,6 @@ impl<'tcx> ConstToPat<'tcx> { span, infcx, param_env: pat_ctxt.param_env, - saw_const_match_error: Cell::new(None), treat_byte_string_as_slice: pat_ctxt .typeck_results .treat_byte_string_as_slice @@ -91,116 +82,55 @@ impl<'tcx> ConstToPat<'tcx> { ty.is_structural_eq_shallow(self.infcx.tcx) } - fn to_pat(&mut self, cv: mir::Const<'tcx>) -> Box> { + fn to_pat(&mut self, c: ty::Const<'tcx>, ty: Ty<'tcx>) -> Box> { trace!(self.treat_byte_string_as_slice); - // This method is just a wrapper handling a validity check; the heavy lifting is - // performed by the recursive `recur` method, which is not meant to be - // invoked except by this method. - // - // once indirect_structural_match is a full fledged error, this - // level of indirection can be eliminated + let pat_from_kind = |kind| Box::new(Pat { span: self.span, ty, kind }); - let have_valtree = - matches!(cv, mir::Const::Ty(_, c) if matches!(c.kind(), ty::ConstKind::Value(_, _))); - let inlined_const_as_pat = match cv { - mir::Const::Ty(_, c) => match c.kind() { - ty::ConstKind::Param(_) - | ty::ConstKind::Infer(_) - | ty::ConstKind::Bound(_, _) - | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Unevaluated(_) - | ty::ConstKind::Error(_) - | ty::ConstKind::Expr(_) => { - span_bug!(self.span, "unexpected const in `to_pat`: {:?}", c.kind()) - } - ty::ConstKind::Value(ty, valtree) => { - self.recur(valtree, ty).unwrap_or_else(|_: FallbackToOpaqueConst| { - Box::new(Pat { - span: self.span, - ty: cv.ty(), - kind: PatKind::Constant { value: cv }, - }) - }) - } - }, - mir::Const::Unevaluated(_, _) => { - span_bug!(self.span, "unevaluated const in `to_pat`: {cv:?}") + // Get a valtree. If that fails, this const is definitely not valid for use as a pattern. + let valtree = match c.eval_valtree(self.tcx(), self.param_env, self.span) { + Ok((_, valtree)) => valtree, + Err(Either::Right(e)) => { + let err = match e { + ErrorHandled::Reported(..) => { + // Let's tell the use where this failing const occurs. + self.tcx().dcx().emit_err(CouldNotEvalConstPattern { span: self.span }) + } + ErrorHandled::TooGeneric(_) => self + .tcx() + .dcx() + .emit_err(ConstPatternDependsOnGenericParameter { span: self.span }), + }; + return pat_from_kind(PatKind::Error(err)); + } + Err(Either::Left(bad_ty)) => { + // The pattern cannot be turned into a valtree. + let e = match bad_ty.kind() { + ty::Adt(def, ..) => { + assert!(def.is_union()); + self.tcx().dcx().emit_err(UnionPattern { span: self.span }) + } + ty::FnPtr(..) | ty::RawPtr(..) => { + self.tcx().dcx().emit_err(PointerPattern { span: self.span }) + } + _ => self + .tcx() + .dcx() + .emit_err(InvalidPattern { span: self.span, non_sm_ty: bad_ty }), + }; + return pat_from_kind(PatKind::Error(e)); } - mir::Const::Val(_, _) => Box::new(Pat { - span: self.span, - ty: cv.ty(), - kind: PatKind::Constant { value: cv }, - }), }; - if self.saw_const_match_error.get().is_none() { - // If we were able to successfully convert the const to some pat (possibly with some - // lints, but no errors), double-check that all types in the const implement - // `PartialEq`. Even if we have a valtree, we may have found something - // in there with non-structural-equality, meaning we match using `PartialEq` - // and we hence have to check if that impl exists. - // This is all messy but not worth cleaning up: at some point we'll emit - // a hard error when we don't have a valtree or when we find something in - // the valtree that is not structural; then this can all be made a lot simpler. - - let structural = traits::search_for_structural_match_violation(self.tcx(), cv.ty()); - debug!( - "search_for_structural_match_violation cv.ty: {:?} returned: {:?}", - cv.ty(), - structural - ); - - if let Some(non_sm_ty) = structural { - if !self.type_has_partial_eq_impl(cv.ty()) { - // This is reachable and important even if we have a valtree: there might be - // non-structural things in a valtree, in which case we fall back to `PartialEq` - // comparison, in which case we better make sure the trait is implemented for - // each inner type (and not just for the surrounding type). - let e = if let ty::Adt(def, ..) = non_sm_ty.kind() { - if def.is_union() { - let err = UnionPattern { span: self.span }; - self.tcx().dcx().emit_err(err) - } else { - // fatal avoids ICE from resolution of nonexistent method (rare case). - self.tcx() - .dcx() - .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty }) - } - } else { - let err = InvalidPattern { span: self.span, non_sm_ty }; - self.tcx().dcx().emit_err(err) - }; - // All branches above emitted an error. Don't print any more lints. - // We errored. Signal that in the pattern, so that follow up errors can be silenced. - let kind = PatKind::Error(e); - return Box::new(Pat { span: self.span, ty: cv.ty(), kind }); - } else if !have_valtree { - // Not being structural prevented us from constructing a valtree, - // so this is definitely a case we want to reject. - let err = TypeNotStructural { span: self.span, non_sm_ty }; - let e = self.tcx().dcx().emit_err(err); - let kind = PatKind::Error(e); - return Box::new(Pat { span: self.span, ty: cv.ty(), kind }); - } else { - // This could be a violation in an inactive enum variant. - // Since we have a valtree, we trust that we have traversed the full valtree and - // complained about structural match violations there, so we don't - // have to check anything any more. - } - } else if !have_valtree { - // The only way valtree construction can fail without the structural match - // checker finding a violation is if there is a pointer somewhere. - let e = self.tcx().dcx().emit_err(PointerPattern { span: self.span }); - let kind = PatKind::Error(e); - return Box::new(Pat { span: self.span, ty: cv.ty(), kind }); - } + // Convert the valtree to a const. + let inlined_const_as_pat = self.valtree_to_pat(valtree, ty); + if !inlined_const_as_pat.references_error() { // Always check for `PartialEq` if we had no other errors yet. - if !self.type_has_partial_eq_impl(cv.ty()) { - let err = TypeNotPartialEq { span: self.span, non_peq_ty: cv.ty() }; + if !self.type_has_partial_eq_impl(ty) { + let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty }; let e = self.tcx().dcx().emit_err(err); let kind = PatKind::Error(e); - return Box::new(Pat { span: self.span, ty: cv.ty(), kind }); + return Box::new(Pat { span: self.span, ty: ty, kind }); } } @@ -243,40 +173,31 @@ impl<'tcx> ConstToPat<'tcx> { fn field_pats( &self, vals: impl Iterator, Ty<'tcx>)>, - ) -> Result>, FallbackToOpaqueConst> { + ) -> Vec> { vals.enumerate() .map(|(idx, (val, ty))| { let field = FieldIdx::new(idx); // Patterns can only use monomorphic types. let ty = self.tcx().normalize_erasing_regions(self.param_env, ty); - Ok(FieldPat { field, pattern: self.recur(val, ty)? }) + FieldPat { field, pattern: self.valtree_to_pat(val, ty) } }) .collect() } // Recursive helper for `to_pat`; invoke that (instead of calling this directly). #[instrument(skip(self), level = "debug")] - fn recur( - &self, - cv: ValTree<'tcx>, - ty: Ty<'tcx>, - ) -> Result>, FallbackToOpaqueConst> { + fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box> { let span = self.span; let tcx = self.tcx(); let param_env = self.param_env; let kind = match ty.kind() { - ty::FnDef(..) => { - let e = tcx.dcx().emit_err(InvalidPattern { span, non_sm_ty: ty }); - self.saw_const_match_error.set(Some(e)); - // We errored. Signal that in the pattern, so that follow up errors can be silenced. - PatKind::Error(e) - } ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => { + // Extremely important check for all ADTs! Make sure they opted-in to be used in + // patterns. debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty,); let err = TypeNotStructural { span, non_sm_ty: ty }; let e = tcx.dcx().emit_err(err); - self.saw_const_match_error.set(Some(e)); // We errored. Signal that in the pattern, so that follow up errors can be silenced. PatKind::Error(e) } @@ -294,13 +215,9 @@ impl<'tcx> ConstToPat<'tcx> { .iter() .map(|field| field.ty(self.tcx(), args)), ), - )?, + ), } } - ty::Tuple(fields) => PatKind::Leaf { - subpatterns: self - .field_pats(cv.unwrap_branch().iter().copied().zip(fields.iter()))?, - }, ty::Adt(def, args) => { assert!(!def.is_union()); // Valtree construction would never succeed for unions. PatKind::Leaf { @@ -311,15 +228,18 @@ impl<'tcx> ConstToPat<'tcx> { .iter() .map(|field| field.ty(self.tcx(), args)), ), - )?, + ), } } + ty::Tuple(fields) => PatKind::Leaf { + subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(fields.iter())), + }, ty::Slice(elem_ty) => PatKind::Slice { prefix: cv .unwrap_branch() .iter() - .map(|val| self.recur(*val, *elem_ty)) - .collect::>()?, + .map(|val| self.valtree_to_pat(*val, *elem_ty)) + .collect(), slice: None, suffix: Box::new([]), }, @@ -327,8 +247,8 @@ impl<'tcx> ConstToPat<'tcx> { prefix: cv .unwrap_branch() .iter() - .map(|val| self.recur(*val, *elem_ty)) - .collect::>()?, + .map(|val| self.valtree_to_pat(*val, *elem_ty)) + .collect(), slice: None, suffix: Box::new([]), }, @@ -361,7 +281,7 @@ impl<'tcx> ConstToPat<'tcx> { _ => *pointee_ty, }; // References have the same valtree representation as their pointee. - let subpattern = self.recur(cv, pointee_ty)?; + let subpattern = self.valtree_to_pat(cv, pointee_ty); PatKind::Deref { subpattern } } } @@ -378,8 +298,7 @@ impl<'tcx> ConstToPat<'tcx> { // NaNs are not ever equal to anything so they make no sense as patterns. // Also see . let e = tcx.dcx().emit_err(NaNPattern { span }); - self.saw_const_match_error.set(Some(e)); - return Err(FallbackToOpaqueConst); + PatKind::Error(e) } else { PatKind::Constant { value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)), @@ -399,12 +318,11 @@ impl<'tcx> ConstToPat<'tcx> { _ => { let err = InvalidPattern { span, non_sm_ty: ty }; let e = tcx.dcx().emit_err(err); - self.saw_const_match_error.set(Some(e)); // We errored. Signal that in the pattern, so that follow up errors can be silenced. PatKind::Error(e) } }; - Ok(Box::new(Pat { span, ty, kind })) + Box::new(Pat { span, ty, kind }) } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index fd778ef78a3ee..622651800f44c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -14,8 +14,7 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd}; use rustc_index::Idx; use rustc_lint as lint; -use rustc_middle::mir::interpret::{ErrorHandled, GlobalId, LitToConstError, LitToConstInput}; -use rustc_middle::mir::{self, Const}; +use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; use rustc_middle::thir::{ Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, }; @@ -549,89 +548,36 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { _ => return pat_from_kind(self.lower_variant_or_leaf(res, id, span, ty, vec![])), }; - // Use `Reveal::All` here because patterns are always monomorphic even if their function - // isn't. - let param_env_reveal_all = self.param_env.with_reveal_all_normalized(self.tcx); - // N.B. There is no guarantee that args collected in typeck results are fully normalized, - // so they need to be normalized in order to pass to `Instance::resolve`, which will ICE - // if given unnormalized types. - let args = self - .tcx - .normalize_erasing_regions(param_env_reveal_all, self.typeck_results.node_args(id)); - let instance = match ty::Instance::try_resolve(self.tcx, param_env_reveal_all, def_id, args) - { - Ok(Some(i)) => i, - Ok(None) => { - // It should be assoc consts if there's no error but we cannot resolve it. - debug_assert!(is_associated_const); - - let e = self.tcx.dcx().emit_err(AssocConstInPattern { span }); - return pat_from_kind(PatKind::Error(e)); - } - - Err(_) => { - let e = self.tcx.dcx().emit_err(CouldNotEvalConstPattern { span }); - return pat_from_kind(PatKind::Error(e)); - } - }; + let args = self.typeck_results.node_args(id); + let c = ty::Const::new_unevaluated(self.tcx, ty::UnevaluatedConst { def: def_id, args }); + let pattern = self.const_to_pat(c, ty, id, span); - let cid = GlobalId { instance, promoted: None }; - // Prefer valtrees over opaque constants. - let const_value = self - .tcx - .const_eval_global_id_for_typeck(param_env_reveal_all, cid, span) - .map(|val| match val { - Some(valtree) => mir::Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)), - None => mir::Const::Val( - self.tcx - .const_eval_global_id(param_env_reveal_all, cid, span) - .expect("const_eval_global_id_for_typeck should have already failed"), - ty, - ), - }); - - match const_value { - Ok(const_) => { - let pattern = self.const_to_pat(const_, id, span); - - if !is_associated_const { - return pattern; - } + if !is_associated_const { + return pattern; + } - let user_provided_types = self.typeck_results().user_provided_types(); - if let Some(&user_ty) = user_provided_types.get(id) { - let annotation = CanonicalUserTypeAnnotation { - user_ty: Box::new(user_ty), - span, - inferred_ty: self.typeck_results().node_type(id), - }; - Box::new(Pat { - span, - kind: PatKind::AscribeUserType { - subpattern: pattern, - ascription: Ascription { - annotation, - // Note that use `Contravariant` here. See the - // `variance` field documentation for details. - variance: ty::Contravariant, - }, - }, - ty: const_.ty(), - }) - } else { - pattern - } - } - Err(ErrorHandled::TooGeneric(_)) => { - // While `Reported | Linted` cases will have diagnostics emitted already - // it is not true for TooGeneric case, so we need to give user more information. - let e = self.tcx.dcx().emit_err(ConstPatternDependsOnGenericParameter { span }); - pat_from_kind(PatKind::Error(e)) - } - Err(_) => { - let e = self.tcx.dcx().emit_err(CouldNotEvalConstPattern { span }); - pat_from_kind(PatKind::Error(e)) - } + let user_provided_types = self.typeck_results().user_provided_types(); + if let Some(&user_ty) = user_provided_types.get(id) { + let annotation = CanonicalUserTypeAnnotation { + user_ty: Box::new(user_ty), + span, + inferred_ty: self.typeck_results().node_type(id), + }; + Box::new(Pat { + span, + kind: PatKind::AscribeUserType { + subpattern: pattern, + ascription: Ascription { + annotation, + // Note that use `Contravariant` here. See the + // `variance` field documentation for details. + variance: ty::Contravariant, + }, + }, + ty, + }) + } else { + pattern } } @@ -662,7 +608,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }; if let Some(lit_input) = lit_input { match tcx.at(expr.span).lit_to_const(lit_input) { - Ok(c) => return self.const_to_pat(Const::Ty(ty, c), id, span).kind, + Ok(c) => return self.const_to_pat(c, ty, id, span).kind, // If an error occurred, ignore that it's a literal // and leave reporting the error up to const eval of // the unevaluated constant below. @@ -675,33 +621,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { tcx.erase_regions(ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id)); let args = ty::InlineConstArgs::new(tcx, ty::InlineConstArgsParts { parent_args, ty }).args; - let uneval = mir::UnevaluatedConst { def: def_id.to_def_id(), args, promoted: None }; debug_assert!(!args.has_free_regions()); let ct = ty::UnevaluatedConst { def: def_id.to_def_id(), args }; - // First try using a valtree in order to destructure the constant into a pattern. - // FIXME: replace "try to do a thing, then fall back to another thing" - // but something more principled, like a trait query checking whether this can be turned into a valtree. - if let Ok(Some(valtree)) = self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, span) - { - let subpattern = self.const_to_pat( - Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)), - id, - span, - ); - PatKind::InlineConstant { subpattern, def: def_id } - } else { - // If that fails, convert it to an opaque constant pattern. - match tcx.const_eval_resolve(self.param_env, uneval, span) { - Ok(val) => self.const_to_pat(mir::Const::Val(val, ty), id, span).kind, - Err(ErrorHandled::TooGeneric(_)) => { - // If we land here it means the const can't be evaluated because it's `TooGeneric`. - let e = self.tcx.dcx().emit_err(ConstPatternDependsOnGenericParameter { span }); - PatKind::Error(e) - } - Err(ErrorHandled::Reported(err, ..)) => PatKind::Error(err.into()), - } - } + let subpattern = self.const_to_pat(ty::Const::new_unevaluated(self.tcx, ct), ty, id, span); + PatKind::InlineConstant { subpattern, def: def_id } } /// Converts literals, paths and negation of literals to patterns. @@ -729,9 +653,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let ct_ty = self.typeck_results.expr_ty(expr); let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg }; match self.tcx.at(expr.span).lit_to_const(lit_input) { - Ok(constant) => { - self.const_to_pat(Const::Ty(ct_ty, constant), expr.hir_id, lit.span).kind - } + Ok(constant) => self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind, Err(LitToConstError::Reported(e)) => PatKind::Error(e), Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), } diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml index 07cd4ae68d9a2..79d2107b2a0a2 100644 --- a/compiler/rustc_next_trait_solver/Cargo.toml +++ b/compiler/rustc_next_trait_solver/Cargo.toml @@ -20,10 +20,10 @@ tracing = "0.1" [features] default = ["nightly"] nightly = [ + "dep:rustc_data_structures", + "dep:rustc_macros", + "dep:rustc_serialize", "rustc_ast_ir/nightly", - "rustc_data_structures", "rustc_index/nightly", - "rustc_macros", - "rustc_serialize", "rustc_type_ir/nightly", ] diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index c2201b1c41ec9..c79dad3953ba0 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -388,6 +388,9 @@ parse_invalid_dyn_keyword = invalid `dyn` keyword parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else` parse_invalid_identifier_with_leading_number = identifiers cannot start with a number +parse_invalid_label = + invalid label name `{$name}` + parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid .label = invalid suffix `{$suffix}` .tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases @@ -414,6 +417,9 @@ parse_invalid_unicode_escape = invalid unicode character escape parse_invalid_variable_declaration = invalid variable declaration +parse_keyword_lifetime = + lifetimes cannot use keyword names + parse_kw_bad_case = keyword `{$kw}` is written in the wrong case .suggestion = write it in the correct case @@ -518,6 +524,8 @@ parse_mismatched_closing_delimiter = mismatched closing delimiter: `{$delimiter} .label_opening_candidate = closing delimiter possibly meant for this .label_unclosed = unclosed delimiter +parse_misplaced_return_type = place the return type after the function parameters + parse_missing_comma_after_match_arm = expected `,` following `match` arm .suggestion = missing a comma here to end this `match` arm diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 092a2a10ab7b3..109d36fe68998 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -660,9 +660,8 @@ pub(crate) struct RemoveLet { #[diag(parse_use_eq_instead)] pub(crate) struct UseEqInstead { #[primary_span] + #[suggestion(style = "verbose", applicability = "machine-applicable", code = "=")] pub span: Span, - #[suggestion(style = "verbose", applicability = "machine-applicable", code = "")] - pub suggestion: Span, } #[derive(Diagnostic)] @@ -1503,6 +1502,20 @@ pub(crate) struct FnPtrWithGenerics { pub sugg: Option, } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + parse_misplaced_return_type, + style = "verbose", + applicability = "maybe-incorrect" +)] +pub(crate) struct MisplacedReturnType { + #[suggestion_part(code = " {snippet}")] + pub fn_params_end: Span, + pub snippet: String, + #[suggestion_part(code = "")] + pub ret_ty_span: Span, +} + #[derive(Subdiagnostic)] #[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")] pub(crate) struct FnPtrWithGenericsSugg { @@ -1517,7 +1530,6 @@ pub(crate) struct FnPtrWithGenericsSugg { pub(crate) struct FnTraitMissingParen { pub span: Span, - pub machine_applicable: bool, } impl Subdiagnostic for FnTraitMissingParen { @@ -1527,16 +1539,11 @@ impl Subdiagnostic for FnTraitMissingParen { _: &F, ) { diag.span_label(self.span, crate::fluent_generated::parse_fn_trait_missing_paren); - let applicability = if self.machine_applicable { - Applicability::MachineApplicable - } else { - Applicability::MaybeIncorrect - }; diag.span_suggestion_short( self.span.shrink_to_hi(), crate::fluent_generated::parse_add_paren, "()", - applicability, + Applicability::MachineApplicable, ); } } @@ -2009,6 +2016,21 @@ pub struct CannotBeRawIdent { pub ident: Symbol, } +#[derive(Diagnostic)] +#[diag(parse_keyword_lifetime)] +pub struct KeywordLifetime { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_invalid_label)] +pub struct InvalidLabel { + #[primary_span] + pub span: Span, + pub name: Symbol, +} + #[derive(Diagnostic)] #[diag(parse_cr_doc_comment)] pub struct CrDocComment { diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index a8fe35f45b31e..0b2c304403941 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -124,7 +124,7 @@ impl<'a> Parser<'a> { if this.eat(&token::Not) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer }; this.expect(&token::OpenDelim(Delimiter::Bracket))?; - let item = this.parse_attr_item(false)?; + let item = this.parse_attr_item(ForceCollect::No)?; this.expect(&token::CloseDelim(Delimiter::Bracket))?; let attr_sp = lo.to(this.prev_token.span); @@ -248,16 +248,15 @@ impl<'a> Parser<'a> { /// PATH /// PATH `=` UNSUFFIXED_LIT /// The delimiters or `=` are still put into the resulting token stream. - pub fn parse_attr_item(&mut self, capture_tokens: bool) -> PResult<'a, ast::AttrItem> { + pub fn parse_attr_item(&mut self, force_collect: ForceCollect) -> PResult<'a, ast::AttrItem> { maybe_whole!(self, NtMeta, |attr| attr.into_inner()); - let do_parse = |this: &mut Self| { + let do_parse = |this: &mut Self, _empty_attrs| { let is_unsafe = this.eat_keyword(kw::Unsafe); let unsafety = if is_unsafe { let unsafe_span = this.prev_token.span; this.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span); this.expect(&token::OpenDelim(Delimiter::Parenthesis))?; - ast::Safety::Unsafe(unsafe_span) } else { ast::Safety::Default @@ -268,10 +267,10 @@ impl<'a> Parser<'a> { if is_unsafe { this.expect(&token::CloseDelim(Delimiter::Parenthesis))?; } - Ok(ast::AttrItem { unsafety, path, args, tokens: None }) + Ok((ast::AttrItem { unsafety, path, args, tokens: None }, false)) }; - // Attr items don't have attributes - if capture_tokens { self.collect_tokens_no_attrs(do_parse) } else { do_parse(self) } + // Attr items don't have attributes. + self.collect_tokens_trailing_token(AttrWrapper::empty(), force_collect, do_parse) } /// Parses attributes that appear after the opening of an item. These should @@ -303,17 +302,13 @@ impl<'a> Parser<'a> { None }; if let Some(attr) = attr { - let end_pos = self.num_bump_calls; - // If we are currently capturing tokens, mark the location of this inner attribute. - // If capturing ends up creating a `LazyAttrTokenStream`, we will include - // this replace range with it, removing the inner attribute from the final - // `AttrTokenStream`. Inner attributes are stored in the parsed AST note. - // During macro expansion, they are selectively inserted back into the - // token stream (the first inner attribute is removed each time we invoke the - // corresponding macro). - let range = start_pos..end_pos; + // If we are currently capturing tokens (i.e. we are within a call to + // `Parser::collect_tokens_trailing_tokens`) record the token positions of this + // inner attribute, for possible later processing in a `LazyAttrTokenStream`. if let Capturing::Yes = self.capture_state.capturing { - self.capture_state.inner_attr_ranges.insert(attr.id, (range, None)); + let end_pos = self.num_bump_calls; + let range = start_pos..end_pos; + self.capture_state.inner_attr_ranges.insert(attr.id, range); } attrs.push(attr); } else { @@ -344,7 +339,7 @@ impl<'a> Parser<'a> { let mut expanded_attrs = Vec::with_capacity(1); while self.token.kind != token::Eof { let lo = self.token.span; - let item = self.parse_attr_item(true)?; + let item = self.parse_attr_item(ForceCollect::Yes)?; expanded_attrs.push((item, lo.to(self.prev_token.span))); if !self.eat(&token::Comma) { break; @@ -463,7 +458,8 @@ impl<'a> Parser<'a> { } } -/// The attributes are complete if all attributes are either a doc comment or a builtin attribute other than `cfg_attr` +/// The attributes are complete if all attributes are either a doc comment or a +/// builtin attribute other than `cfg_attr`. pub fn is_complete(attrs: &[ast::Attribute]) -> bool { attrs.iter().all(|attr| { attr.is_doc_comment() diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 1123c31f55135..dc5f98f7be8b6 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -1,5 +1,5 @@ -use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCursor, TrailingToken}; -use rustc_ast::token::{self, Delimiter, Token, TokenKind}; +use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCursor}; +use rustc_ast::token::{Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{AttrTokenStream, AttrTokenTree, AttrsTarget, DelimSpacing}; use rustc_ast::tokenstream::{DelimSpan, LazyAttrTokenStream, Spacing, ToAttrTokenStream}; use rustc_ast::{self as ast}; @@ -17,12 +17,12 @@ use std::{iter, mem}; /// /// This wrapper prevents direct access to the underlying `ast::AttrVec`. /// Parsing code can only get access to the underlying attributes -/// by passing an `AttrWrapper` to `collect_tokens_trailing_tokens`. +/// by passing an `AttrWrapper` to `collect_tokens_trailing_token`. /// This makes it difficult to accidentally construct an AST node /// (which stores an `ast::AttrVec`) without first collecting tokens. /// /// This struct has its own module, to ensure that the parser code -/// cannot directly access the `attrs` field +/// cannot directly access the `attrs` field. #[derive(Debug, Clone)] pub struct AttrWrapper { attrs: AttrVec, @@ -76,14 +76,13 @@ fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool { }) } -// Produces a `TokenStream` on-demand. Using `cursor_snapshot` -// and `num_calls`, we can reconstruct the `TokenStream` seen -// by the callback. This allows us to avoid producing a `TokenStream` -// if it is never needed - for example, a captured `macro_rules!` -// argument that is never passed to a proc macro. -// In practice token stream creation happens rarely compared to -// calls to `collect_tokens` (see some statistics in #78736), -// so we are doing as little up-front work as possible. +// From a value of this type we can reconstruct the `TokenStream` seen by the +// `f` callback passed to a call to `Parser::collect_tokens_trailing_token`, by +// replaying the getting of the tokens. This saves us producing a `TokenStream` +// if it is never needed, e.g. a captured `macro_rules!` argument that is never +// passed to a proc macro. In practice, token stream creation happens rarely +// compared to calls to `collect_tokens` (see some statistics in #78736) so we +// are doing as little up-front work as possible. // // This also makes `Parser` very cheap to clone, since // there is no intermediate collection buffer to clone. @@ -163,44 +162,55 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { } impl<'a> Parser<'a> { - /// Records all tokens consumed by the provided callback, - /// including the current token. These tokens are collected - /// into a `LazyAttrTokenStream`, and returned along with the result - /// of the callback. + /// Parses code with `f`. If appropriate, it records the tokens (in + /// `LazyAttrTokenStream` form) that were parsed in the result, accessible + /// via the `HasTokens` trait. The second (bool) part of the callback's + /// result indicates if an extra token should be captured, e.g. a comma or + /// semicolon. /// /// The `attrs` passed in are in `AttrWrapper` form, which is opaque. The /// `AttrVec` within is passed to `f`. See the comment on `AttrWrapper` for /// details. /// - /// Note: If your callback consumes an opening delimiter - /// (including the case where you call `collect_tokens` - /// when the current token is an opening delimiter), - /// you must also consume the corresponding closing delimiter. + /// Note: If your callback consumes an opening delimiter (including the + /// case where `self.token` is an opening delimiter on entry to this + /// function), you must also consume the corresponding closing delimiter. + /// E.g. you can consume `something ([{ }])` or `([{}])`, but not `([{}]`. + /// This restriction isn't a problem in practice, because parsed AST items + /// always have matching delimiters. /// - /// That is, you can consume - /// `something ([{ }])` or `([{}])`, but not `([{}]` - /// - /// This restriction shouldn't be an issue in practice, - /// since this function is used to record the tokens for - /// a parsed AST item, which always has matching delimiters. + /// The following example code will be used to explain things in comments + /// below. It has an outer attribute and an inner attribute. Parsing it + /// involves two calls to this method, one of which is indirectly + /// recursive. + /// ```ignore (fake attributes) + /// #[cfg_eval] // token pos + /// mod m { // 0.. 3 + /// #[cfg_attr(cond1, attr1)] // 3..12 + /// fn g() { // 12..17 + /// #![cfg_attr(cond2, attr2)] // 17..27 + /// let _x = 3; // 27..32 + /// } // 32..33 + /// } // 33..34 + /// ``` pub fn collect_tokens_trailing_token( &mut self, attrs: AttrWrapper, force_collect: ForceCollect, - f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, (R, TrailingToken)>, + f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, (R, bool)>, ) -> PResult<'a, R> { - // We only bail out when nothing could possibly observe the collected tokens: - // 1. We cannot be force collecting tokens (since force-collecting requires tokens - // by definition + // Skip collection when nothing could observe the collected tokens, i.e. + // all of the following conditions hold. + // - We are not force collecting tokens (because force collection + // requires tokens by definition). if matches!(force_collect, ForceCollect::No) - // None of our outer attributes can require tokens (e.g. a proc-macro) + // - None of our outer attributes require tokens. && attrs.is_complete() - // If our target supports custom inner attributes, then we cannot bail - // out early, since we may need to capture tokens for a custom inner attribute - // invocation. + // - Our target doesn't support custom inner attributes (custom + // inner attribute invocation might require token capturing). && !R::SUPPORTS_CUSTOM_INNER_ATTRS - // Never bail out early in `capture_cfg` mode, since there might be `#[cfg]` - // or `#[cfg_attr]` attributes. + // - We are not in `capture_cfg` mode (which requires tokens if + // the parsed node has `#[cfg]` or `#[cfg_attr]` attributes). && !self.capture_cfg { return Ok(f(self, attrs.attrs)?.0); @@ -212,81 +222,68 @@ impl<'a> Parser<'a> { let has_outer_attrs = !attrs.attrs.is_empty(); let replace_ranges_start = self.capture_state.replace_ranges.len(); - let (mut ret, trailing) = { + // We set and restore `Capturing::Yes` on either side of the call to + // `f`, so we can distinguish the outermost call to + // `collect_tokens_trailing_token` (e.g. parsing `m` in the example + // above) from any inner (indirectly recursive) calls (e.g. parsing `g` + // in the example above). This distinction is used below and in + // `Parser::parse_inner_attributes`. + let (mut ret, capture_trailing) = { let prev_capturing = mem::replace(&mut self.capture_state.capturing, Capturing::Yes); let ret_and_trailing = f(self, attrs.attrs); self.capture_state.capturing = prev_capturing; ret_and_trailing? }; - // When we're not in `capture-cfg` mode, then bail out early if: - // 1. Our target doesn't support tokens at all (e.g we're parsing an `NtIdent`) - // so there's nothing for us to do. - // 2. Our target already has tokens set (e.g. we've parsed something - // like `#[my_attr] $item`). The actual parsing code takes care of - // prepending any attributes to the nonterminal, so we don't need to - // modify the already captured tokens. - // Note that this check is independent of `force_collect`- if we already - // have tokens, or can't even store them, then there's never a need to - // force collection of new tokens. + // When we're not in `capture_cfg` mode, then skip collecting and + // return early if either of the following conditions hold. + // - `None`: Our target doesn't support tokens at all (e.g. `NtIdent`). + // - `Some(Some(_))`: Our target already has tokens set (e.g. we've + // parsed something like `#[my_attr] $item`). The actual parsing code + // takes care of prepending any attributes to the nonterminal, so we + // don't need to modify the already captured tokens. + // + // Note that this check is independent of `force_collect`. There's no + // need to collect tokens when we don't support tokens or already have + // tokens. if !self.capture_cfg && matches!(ret.tokens_mut(), None | Some(Some(_))) { return Ok(ret); } - // This is very similar to the bail out check at the start of this function. - // Now that we've parsed an AST node, we have more information available. + // This is similar to the "skip collection" check at the start of this + // function, but now that we've parsed an AST node we have more + // information available. (If we return early here that means the + // setup, such as cloning the token cursor, was unnecessary. That's + // hard to avoid.) + // + // Skip collection when nothing could observe the collected tokens, i.e. + // all of the following conditions hold. + // - We are not force collecting tokens. if matches!(force_collect, ForceCollect::No) - // We now have inner attributes available, so this check is more precise - // than `attrs.is_complete()` at the start of the function. - // As a result, we don't need to check `R::SUPPORTS_CUSTOM_INNER_ATTRS` + // - None of our outer *or* inner attributes require tokens. + // (`attrs` was just outer attributes, but `ret.attrs()` is outer + // and inner attributes. That makes this check more precise than + // `attrs.is_complete()` at the start of the function, and we can + // skip the subsequent check on `R::SUPPORTS_CUSTOM_INNER_ATTRS`. && crate::parser::attr::is_complete(ret.attrs()) - // Subtle: We call `has_cfg_or_cfg_attr` with the attrs from `ret`. - // This ensures that we consider inner attributes (e.g. `#![cfg]`), - // which require us to have tokens available - // We also call `has_cfg_or_cfg_attr` at the beginning of this function, - // but we only bail out if there's no possibility of inner attributes - // (!R::SUPPORTS_CUSTOM_INNER_ATTRS) - // We only capture about `#[cfg]` or `#[cfg_attr]` in `capture_cfg` - // mode - during normal parsing, we don't need any special capturing - // for those attributes, since they're builtin. - && !(self.capture_cfg && has_cfg_or_cfg_attr(ret.attrs())) + // - We are not in `capture_cfg` mode, or we are but there are no + // `#[cfg]` or `#[cfg_attr]` attributes. (During normal + // non-`capture_cfg` parsing, we don't need any special capturing + // for those attributes, because they're builtin.) + && (!self.capture_cfg || !has_cfg_or_cfg_attr(ret.attrs())) { return Ok(ret); } - let mut inner_attr_replace_ranges = Vec::new(); - // Take the captured ranges for any inner attributes that we parsed. - for inner_attr in ret.attrs().iter().filter(|a| a.style == ast::AttrStyle::Inner) { - if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&inner_attr.id) { - inner_attr_replace_ranges.push(attr_range); - } else { - self.dcx().span_delayed_bug(inner_attr.span, "Missing token range for attribute"); - } - } - let replace_ranges_end = self.capture_state.replace_ranges.len(); - // Capture a trailing token if requested by the callback 'f' - let captured_trailing = match trailing { - TrailingToken::None => false, - TrailingToken::Gt => { - assert_eq!(self.token.kind, token::Gt); - false - } - TrailingToken::Semi => { - assert_eq!(self.token.kind, token::Semi); - true - } - TrailingToken::MaybeComma => self.token.kind == token::Comma, - }; - assert!( - !(self.break_last_token && captured_trailing), + !(self.break_last_token && capture_trailing), "Cannot set break_last_token and have trailing token" ); let end_pos = self.num_bump_calls - + captured_trailing as u32 + + capture_trailing as u32 // If we 'broke' the last token (e.g. breaking a '>>' token to two '>' tokens), then // extend the range of captured tokens to include it, since the parser was not actually // bumped past it. When the `LazyAttrTokenStream` gets converted into an @@ -295,15 +292,28 @@ impl<'a> Parser<'a> { let num_calls = end_pos - start_pos; + // Take the captured ranges for any inner attributes that we parsed in + // `Parser::parse_inner_attributes`, and pair them in a `ReplaceRange` + // with `None`, which means the relevant tokens will be removed. (More + // details below.) + let mut inner_attr_replace_ranges = Vec::new(); + for inner_attr in ret.attrs().iter().filter(|a| a.style == ast::AttrStyle::Inner) { + if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&inner_attr.id) { + inner_attr_replace_ranges.push((attr_range, None)); + } else { + self.dcx().span_delayed_bug(inner_attr.span, "Missing token range for attribute"); + } + } + // This is hot enough for `deep-vector` that checking the conditions for an empty iterator // is measurably faster than actually executing the iterator. let replace_ranges: Box<[ReplaceRange]> = if replace_ranges_start == replace_ranges_end && inner_attr_replace_ranges.is_empty() { Box::new([]) } else { - // Grab any replace ranges that occur *inside* the current AST node. - // We will perform the actual replacement when we convert the `LazyAttrTokenStream` - // to an `AttrTokenStream`. + // Grab any replace ranges that occur *inside* the current AST node. We will + // perform the actual replacement only when we convert the `LazyAttrTokenStream` to + // an `AttrTokenStream`. self.capture_state.replace_ranges[replace_ranges_start..replace_ranges_end] .iter() .cloned() @@ -312,6 +322,28 @@ impl<'a> Parser<'a> { .collect() }; + // What is the status here when parsing the example code at the top of this method? + // + // When parsing `g`: + // - `start_pos..end_pos` is `12..33` (`fn g { ... }`, excluding the outer attr). + // - `inner_attr_replace_ranges` has one entry (`5..15`, when counting from `fn`), to + // delete the inner attr's tokens. + // - This entry is put into the lazy tokens for `g`, i.e. deleting the inner attr from + // those tokens (if they get evaluated). + // - Those lazy tokens are also put into an `AttrsTarget` that is appended to `self`'s + // replace ranges at the bottom of this function, for processing when parsing `m`. + // - `replace_ranges_start..replace_ranges_end` is empty. + // + // When parsing `m`: + // - `start_pos..end_pos` is `0..34` (`mod m`, excluding the `#[cfg_eval]` attribute). + // - `inner_attr_replace_ranges` is empty. + // - `replace_range_start..replace_ranges_end` has two entries. + // - One to delete the inner attribute (`17..27`), obtained when parsing `g` (see above). + // - One `AttrsTarget` (added below when parsing `g`) to replace all of `g` (`3..33`, + // including its outer attribute), with: + // - `attrs`: includes the outer and the inner attr. + // - `tokens`: lazy tokens for `g` (with its inner attr deleted). + let tokens = LazyAttrTokenStream::new(LazyAttrTokenStreamImpl { start_token, num_calls, @@ -325,27 +357,37 @@ impl<'a> Parser<'a> { *target_tokens = Some(tokens.clone()); } - let final_attrs = ret.attrs(); - // If `capture_cfg` is set and we're inside a recursive call to // `collect_tokens_trailing_token`, then we need to register a replace range // if we have `#[cfg]` or `#[cfg_attr]`. This allows us to run eager cfg-expansion // on the captured token stream. if self.capture_cfg && matches!(self.capture_state.capturing, Capturing::Yes) - && has_cfg_or_cfg_attr(final_attrs) + && has_cfg_or_cfg_attr(ret.attrs()) { assert!(!self.break_last_token, "Should not have unglued last token with cfg attr"); - // Replace the entire AST node that we just parsed, including attributes, with - // `target`. If this AST node is inside an item that has `#[derive]`, then this will - // allow us to cfg-expand this AST node. + // What is the status here when parsing the example code at the top of this method? + // + // When parsing `g`, we add two entries: + // - The `start_pos..end_pos` (`3..33`) entry has a new `AttrsTarget` with: + // - `attrs`: includes the outer and the inner attr. + // - `tokens`: lazy tokens for `g` (with its inner attr deleted). + // - `inner_attr_replace_ranges` contains the one entry to delete the inner attr's + // tokens (`17..27`). + // + // When parsing `m`, we do nothing here. + + // Set things up so that the entire AST node that we just parsed, including attributes, + // will be replaced with `target` in the lazy token stream. This will allow us to + // cfg-expand this AST node. let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos }; - let target = AttrsTarget { attrs: final_attrs.iter().cloned().collect(), tokens }; + let target = AttrsTarget { attrs: ret.attrs().iter().cloned().collect(), tokens }; self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target))); self.capture_state.replace_ranges.extend(inner_attr_replace_ranges); } else if matches!(self.capture_state.capturing, Capturing::No) { - // Only clear the ranges once we've finished capturing entirely. + // Only clear the ranges once we've finished capturing entirely, i.e. we've finished + // the outermost call to this method. self.capture_state.replace_ranges.clear(); self.capture_state.inner_attr_ranges.clear(); } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 0da7fefe6ed21..1a0d9aa6378e7 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -430,7 +430,7 @@ impl<'a> Parser<'a> { &mut self, edible: &[TokenKind], inedible: &[TokenKind], - ) -> PResult<'a, Recovered> { + ) -> PResult<'a, ErrorGuaranteed> { debug!("expected_one_of_not_found(edible: {:?}, inedible: {:?})", edible, inedible); fn tokens_to_string(tokens: &[TokenType]) -> String { let mut i = tokens.iter(); @@ -533,7 +533,7 @@ impl<'a> Parser<'a> { sugg: ExpectedSemiSugg::ChangeToSemi(self.token.span), }); self.bump(); - return Ok(Recovered::Yes(guar)); + return Ok(guar); } else if self.look_ahead(0, |t| { t == &token::CloseDelim(Delimiter::Brace) || ((t.can_begin_expr() || t.can_begin_item()) @@ -557,7 +557,7 @@ impl<'a> Parser<'a> { unexpected_token_label: Some(self.token.span), sugg: ExpectedSemiSugg::AddSemi(span), }); - return Ok(Recovered::Yes(guar)); + return Ok(guar); } } @@ -566,10 +566,7 @@ impl<'a> Parser<'a> { && expected.iter().any(|tok| matches!(tok, TokenType::Token(TokenKind::Eq))) { // Likely typo: `=` → `==` in let expr or enum item - return Err(self.dcx().create_err(UseEqInstead { - span: self.token.span, - suggestion: self.token.span.with_lo(self.token.span.lo() + BytePos(1)), - })); + return Err(self.dcx().create_err(UseEqInstead { span: self.token.span })); } if self.token.is_keyword(kw::Move) && self.prev_token.is_keyword(kw::Async) { @@ -715,7 +712,7 @@ impl<'a> Parser<'a> { if self.check_too_many_raw_str_terminators(&mut err) { if expected.contains(&TokenType::Token(token::Semi)) && self.eat(&token::Semi) { let guar = err.emit(); - return Ok(Recovered::Yes(guar)); + return Ok(guar); } else { return Err(err); } @@ -2240,11 +2237,11 @@ impl<'a> Parser<'a> { } _ => { // Otherwise, try to get a type and emit a suggestion. - if let Some(ty) = pat.to_ty() { + if let Some(_) = pat.to_ty() { err.span_suggestion_verbose( - pat.span, + pat.span.shrink_to_lo(), "explicitly ignore the parameter name", - format!("_: {}", pprust::ty_to_string(&ty)), + "_: ".to_string(), Applicability::MachineApplicable, ); err.note(rfc_note); @@ -2256,7 +2253,7 @@ impl<'a> Parser<'a> { // `fn foo(a, b) {}`, `fn foo(a, b) {}` or `fn foo(usize, usize) {}` if first_param { - err.span_suggestion( + err.span_suggestion_verbose( self_span, "if this is a `self` type, give it a parameter name", self_sugg, @@ -2266,14 +2263,14 @@ impl<'a> Parser<'a> { // Avoid suggesting that `fn foo(HashMap)` is fixed with a change to // `fn foo(HashMap: TypeName)`. if self.token != token::Lt { - err.span_suggestion( + err.span_suggestion_verbose( param_span, "if this is a parameter name, give it a type", param_sugg, Applicability::HasPlaceholders, ); } - err.span_suggestion( + err.span_suggestion_verbose( type_span, "if this is a type, explicitly ignore the parameter name", type_sugg, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 4bd20be417123..2542108728f7d 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -5,7 +5,7 @@ use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, - SemiColonMode, SeqSep, TokenType, Trailing, TrailingToken, + SemiColonMode, SeqSep, TokenType, Trailing, }; use crate::errors; @@ -785,23 +785,14 @@ impl<'a> Parser<'a> { } }; - self.parse_and_disallow_postfix_after_cast(cast_expr) - } - - /// Parses a postfix operators such as `.`, `?`, or index (`[]`) after a cast, - /// then emits an error and returns the newly parsed tree. - /// The resulting parse tree for `&x as T[0]` has a precedence of `((&x) as T)[0]`. - fn parse_and_disallow_postfix_after_cast( - &mut self, - cast_expr: P, - ) -> PResult<'a, P> { - if let ExprKind::Type(_, _) = cast_expr.kind { - panic!("ExprKind::Type must not be parsed"); - } + // Try to parse a postfix operator such as `.`, `?`, or index (`[]`) + // after a cast. If one is present, emit an error then return a valid + // parse tree; For something like `&x as T[0]` will be as if it was + // written `((&x) as T)[0]`. let span = cast_expr.span; - let with_postfix = self.parse_expr_dot_or_call_with_(cast_expr, span)?; + let with_postfix = self.parse_expr_dot_or_call_with(AttrVec::new(), cast_expr, span)?; // Check if an illegal postfix operator has been added after the cast. // If the resulting expression is not a cast, it is an illegal postfix operator. @@ -885,23 +876,63 @@ impl<'a> Parser<'a> { self.collect_tokens_for_expr(attrs, |this, attrs| { let base = this.parse_expr_bottom()?; let span = this.interpolated_or_expr_span(&base); - this.parse_expr_dot_or_call_with(base, span, attrs) + this.parse_expr_dot_or_call_with(attrs, base, span) }) } pub(super) fn parse_expr_dot_or_call_with( &mut self, - e0: P, - lo: Span, mut attrs: ast::AttrVec, + mut e: P, + lo: Span, ) -> PResult<'a, P> { - // Stitch the list of outer attributes onto the return value. - // A little bit ugly, but the best way given the current code - // structure - let res = ensure_sufficient_stack( - // this expr demonstrates the recursion it guards against - || self.parse_expr_dot_or_call_with_(e0, lo), - ); + let res = ensure_sufficient_stack(|| { + loop { + let has_question = + if self.prev_token.kind == TokenKind::Ident(kw::Return, IdentIsRaw::No) { + // We are using noexpect here because we don't expect a `?` directly after + // a `return` which could be suggested otherwise. + self.eat_noexpect(&token::Question) + } else { + self.eat(&token::Question) + }; + if has_question { + // `expr?` + e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e)); + continue; + } + let has_dot = + if self.prev_token.kind == TokenKind::Ident(kw::Return, IdentIsRaw::No) { + // We are using noexpect here because we don't expect a `.` directly after + // a `return` which could be suggested otherwise. + self.eat_noexpect(&token::Dot) + } else if self.token.kind == TokenKind::RArrow && self.may_recover() { + // Recovery for `expr->suffix`. + self.bump(); + let span = self.prev_token.span; + self.dcx().emit_err(errors::ExprRArrowCall { span }); + true + } else { + self.eat(&token::Dot) + }; + if has_dot { + // expr.f + e = self.parse_dot_suffix_expr(lo, e)?; + continue; + } + if self.expr_is_complete(&e) { + return Ok(e); + } + e = match self.token.kind { + token::OpenDelim(Delimiter::Parenthesis) => self.parse_expr_fn_call(lo, e), + token::OpenDelim(Delimiter::Bracket) => self.parse_expr_index(lo, e)?, + _ => return Ok(e), + } + } + }); + + // Stitch the list of outer attributes onto the return value. A little + // bit ugly, but the best way given the current code structure. if attrs.is_empty() { res } else { @@ -915,50 +946,6 @@ impl<'a> Parser<'a> { } } - fn parse_expr_dot_or_call_with_(&mut self, mut e: P, lo: Span) -> PResult<'a, P> { - loop { - let has_question = - if self.prev_token.kind == TokenKind::Ident(kw::Return, IdentIsRaw::No) { - // we are using noexpect here because we don't expect a `?` directly after a `return` - // which could be suggested otherwise - self.eat_noexpect(&token::Question) - } else { - self.eat(&token::Question) - }; - if has_question { - // `expr?` - e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e)); - continue; - } - let has_dot = if self.prev_token.kind == TokenKind::Ident(kw::Return, IdentIsRaw::No) { - // we are using noexpect here because we don't expect a `.` directly after a `return` - // which could be suggested otherwise - self.eat_noexpect(&token::Dot) - } else if self.token.kind == TokenKind::RArrow && self.may_recover() { - // Recovery for `expr->suffix`. - self.bump(); - let span = self.prev_token.span; - self.dcx().emit_err(errors::ExprRArrowCall { span }); - true - } else { - self.eat(&token::Dot) - }; - if has_dot { - // expr.f - e = self.parse_dot_suffix_expr(lo, e)?; - continue; - } - if self.expr_is_complete(&e) { - return Ok(e); - } - e = match self.token.kind { - token::OpenDelim(Delimiter::Parenthesis) => self.parse_expr_fn_call(lo, e), - token::OpenDelim(Delimiter::Bracket) => self.parse_expr_index(lo, e)?, - _ => return Ok(e), - } - } - } - pub(super) fn parse_dot_suffix_expr( &mut self, lo: Span, @@ -1388,7 +1375,7 @@ impl<'a> Parser<'a> { /// Parses things like parenthesized exprs, macros, `return`, etc. /// /// N.B., this does not parse outer attributes, and is private because it only works - /// correctly if called from `parse_dot_or_call_expr()`. + /// correctly if called from `parse_expr_dot_or_call`. fn parse_expr_bottom(&mut self) -> PResult<'a, P> { maybe_recover_from_interpolated_ty_qpath!(self, true); @@ -2487,7 +2474,7 @@ impl<'a> Parser<'a> { id: DUMMY_NODE_ID, is_placeholder: false, }, - TrailingToken::MaybeComma, + this.token == token::Comma, )) }) } @@ -2932,10 +2919,17 @@ impl<'a> Parser<'a> { } pub(crate) fn eat_label(&mut self) -> Option