From c0a9f722c40b728eef492040332d4616c1393e4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 5 Dec 2023 08:56:30 +0100 Subject: [PATCH 01/17] Undeprecate and use lint `unstable_features` --- compiler/rustc_lexer/src/lib.rs | 5 +-- compiler/rustc_lint/messages.ftl | 2 +- compiler/rustc_lint/src/builtin.rs | 34 +++++++++++++++---- compiler/rustc_parse_format/src/lib.rs | 5 +-- .../feature-gate-feature-gate.stderr | 2 +- 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 43dfd34a6ff74..f6c9289b529c7 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -20,8 +20,9 @@ //! [`rustc_parse::lexer`]: ../rustc_parse/lexer/index.html #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] -// We want to be able to build this crate with a stable compiler, so no -// `#![feature]` attributes should be added. +// We want to be able to build this crate with a stable compiler, +// so no `#![feature]` attributes should be added. +#![deny(unstable_features)] mod cursor; pub mod unescape; diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 40e6b1b579f5b..54ebe5ba912cc 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -148,7 +148,7 @@ lint_builtin_unsafe_impl = implementation of an `unsafe` trait lint_builtin_unsafe_trait = declaration of an `unsafe` trait -lint_builtin_unstable_features = unstable feature +lint_builtin_unstable_features = use of an unstable feature lint_builtin_unused_doc_comment = unused doc comment .label = rustdoc does not generate documentation for {$kind} diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 045ff38c0568c..324a8896a0769 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1233,10 +1233,30 @@ impl<'tcx> LateLintPass<'tcx> for MutableTransmutes { } declare_lint! { - /// The `unstable_features` is deprecated and should no longer be used. + /// The `unstable_features` lint detects uses of `#![feature]`. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unstable_features)] + /// #![feature(test)] + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In larger nightly-based projects which + /// + /// * consist of a multitude of crates where a subset of crates has to compile on + /// stable either unconditionally or depending on a `cfg` flag to for example + /// allow stable users to depend on them, + /// * don't use nightly for experimental features but for, e.g., unstable options only, + /// + /// this lint may come in handy to enforce policies of these kinds. UNSTABLE_FEATURES, Allow, - "enabling unstable features (deprecated. do not use)" + "enabling unstable features" } declare_lint_pass!( @@ -1246,11 +1266,11 @@ declare_lint_pass!( impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { - if attr.has_name(sym::feature) { - if let Some(items) = attr.meta_item_list() { - for item in items { - cx.emit_spanned_lint(UNSTABLE_FEATURES, item.span(), BuiltinUnstableFeatures); - } + if attr.has_name(sym::feature) + && let Some(items) = attr.meta_item_list() + { + for item in items { + cx.emit_spanned_lint(UNSTABLE_FEATURES, item.span(), BuiltinUnstableFeatures); } } } diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index e886db3da2965..e5998bc3e99cb 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -11,8 +11,9 @@ )] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] -// WARNING: We want to be able to build this crate with a stable compiler, -// so no `#![feature]` attributes should be added! +// We want to be able to build this crate with a stable compiler, +// so no `#![feature]` attributes should be added. +#![deny(unstable_features)] use rustc_lexer::unescape; pub use Alignment::*; diff --git a/tests/ui/feature-gates/feature-gate-feature-gate.stderr b/tests/ui/feature-gates/feature-gate-feature-gate.stderr index 8ff99ddbe2188..6ca6c04e40181 100644 --- a/tests/ui/feature-gates/feature-gate-feature-gate.stderr +++ b/tests/ui/feature-gates/feature-gate-feature-gate.stderr @@ -1,4 +1,4 @@ -error: unstable feature +error: use of an unstable feature --> $DIR/feature-gate-feature-gate.rs:2:12 | LL | #![feature(intrinsics)] From bfe04e08c04e2c332eb759d9ae8c5d53144b72b8 Mon Sep 17 00:00:00 2001 From: zachs18 <8355914+zachs18@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:46:45 -0600 Subject: [PATCH 02/17] Fix deallocation with wrong allocator in (A)Rc::from_box_in --- library/alloc/src/rc.rs | 2 +- library/alloc/src/sync.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 263b1449de156..f986df058467b 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1924,7 +1924,7 @@ impl Rc { // Free the allocation without dropping its contents let (bptr, alloc) = Box::into_raw_with_allocator(src); - let src = Box::from_raw(bptr as *mut mem::ManuallyDrop); + let src = Box::from_raw_in(bptr as *mut mem::ManuallyDrop, alloc.by_ref()); drop(src); Self::from_ptr_in(ptr, alloc) diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 5273b3cb2dafa..dc82c9c411110 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1869,7 +1869,7 @@ impl Arc { // Free the allocation without dropping its contents let (bptr, alloc) = Box::into_raw_with_allocator(src); - let src = Box::from_raw(bptr as *mut mem::ManuallyDrop); + let src = Box::from_raw_in(bptr as *mut mem::ManuallyDrop, alloc.by_ref()); drop(src); Self::from_ptr_in(ptr, alloc) From 3b325bc25fdc0459b99ccb3baeb48a4c2459869c Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 14 Nov 2023 14:52:41 +0100 Subject: [PATCH 03/17] Refactor uses of `objc_msgSend` to no longer have clashing definitions This is very similar to what Apple's own headers encourage you to do (cast the function pointer before use instead of making new declarations). Additionally, I'm documenting a few of the memory management rules we're following, ensuring that the `args` function doesn't leak memory (if you wrap it in an autorelease pool). --- library/std/src/sys/pal/unix/args.rs | 81 +++++++++++++++------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs index 9f7dcc0416e52..78e82d9c194c4 100644 --- a/library/std/src/sys/pal/unix/args.rs +++ b/library/std/src/sys/pal/unix/args.rs @@ -201,9 +201,9 @@ mod imp { // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs // and use underscores in their names - they're most probably - // are considered private and therefore should be avoided - // Here is another way to get arguments using Objective C - // runtime + // are considered private and therefore should be avoided. + // Here is another way to get arguments using the Objective-C + // runtime. // // In general it looks like: // res = Vec::new() @@ -213,53 +213,60 @@ mod imp { // res #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] pub fn args() -> Args { - use crate::ffi::OsString; + use crate::ffi::{c_char, c_void, OsString}; use crate::mem; use crate::str; - extern "C" { - fn sel_registerName(name: *const libc::c_uchar) -> Sel; - fn objc_getClass(class_name: *const libc::c_uchar) -> NsId; - } + type Sel = *const c_void; + type NsId = *const c_void; + type NSUInteger = usize; - #[cfg(target_arch = "aarch64")] extern "C" { - fn objc_msgSend(obj: NsId, sel: Sel) -> NsId; - #[allow(clashing_extern_declarations)] - #[link_name = "objc_msgSend"] - fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId; - } + fn sel_registerName(name: *const c_char) -> Sel; + fn objc_getClass(class_name: *const c_char) -> NsId; - #[cfg(not(target_arch = "aarch64"))] - extern "C" { - fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId; - #[allow(clashing_extern_declarations)] - #[link_name = "objc_msgSend"] - fn objc_msgSend_ul(obj: NsId, sel: Sel, ...) -> NsId; + // This must be transmuted to an appropriate function pointer type before being called. + fn objc_msgSend(); } - type Sel = *const libc::c_void; - type NsId = *const libc::c_void; + const MSG_SEND_PTR: unsafe extern "C" fn() = objc_msgSend; + const MSG_SEND_NO_ARGUMENTS_RETURN_PTR: unsafe extern "C" fn(NsId, Sel) -> *const c_void = + unsafe { mem::transmute(MSG_SEND_PTR) }; + const MSG_SEND_NO_ARGUMENTS_RETURN_NSUINTEGER: unsafe extern "C" fn( + NsId, + Sel, + ) -> NSUInteger = unsafe { mem::transmute(MSG_SEND_PTR) }; + const MSG_SEND_NSINTEGER_ARGUMENT_RETURN_PTR: unsafe extern "C" fn( + NsId, + Sel, + NSUInteger, + ) + -> *const c_void = unsafe { mem::transmute(MSG_SEND_PTR) }; let mut res = Vec::new(); unsafe { - let process_info_sel = - sel_registerName(c"processInfo".as_ptr() as *const libc::c_uchar); - let arguments_sel = sel_registerName(c"arguments".as_ptr() as *const libc::c_uchar); - let utf8_sel = sel_registerName(c"UTF8String".as_ptr() as *const libc::c_uchar); - let count_sel = sel_registerName(c"count".as_ptr() as *const libc::c_uchar); - let object_at_sel = - sel_registerName(c"objectAtIndex:".as_ptr() as *const libc::c_uchar); - - let klass = objc_getClass(c"NSProcessInfo".as_ptr() as *const libc::c_uchar); - let info = objc_msgSend(klass, process_info_sel); - let args = objc_msgSend(info, arguments_sel); - - let cnt: usize = mem::transmute(objc_msgSend(args, count_sel)); + let process_info_sel = sel_registerName(c"processInfo".as_ptr()); + let arguments_sel = sel_registerName(c"arguments".as_ptr()); + let count_sel = sel_registerName(c"count".as_ptr()); + let object_at_index_sel = sel_registerName(c"objectAtIndex:".as_ptr()); + let utf8string_sel = sel_registerName(c"UTF8String".as_ptr()); + + let klass = objc_getClass(c"NSProcessInfo".as_ptr()); + // `+[NSProcessInfo processInfo]` returns an object with +0 retain count, so no need to manually `retain/release`. + let info = MSG_SEND_NO_ARGUMENTS_RETURN_PTR(klass, process_info_sel); + + // `-[NSProcessInfo arguments]` returns an object with +0 retain count, so no need to manually `retain/release`. + let args = MSG_SEND_NO_ARGUMENTS_RETURN_PTR(info, arguments_sel); + + let cnt = MSG_SEND_NO_ARGUMENTS_RETURN_NSUINTEGER(args, count_sel); for i in 0..cnt { - let tmp = objc_msgSend_ul(args, object_at_sel, i as libc::c_ulong); - let utf_c_str: *const libc::c_char = mem::transmute(objc_msgSend(tmp, utf8_sel)); + // `-[NSArray objectAtIndex:]` returns an object whose lifetime is tied to the array, so no need to manually `retain/release`. + let ns_string = + MSG_SEND_NSINTEGER_ARGUMENT_RETURN_PTR(args, object_at_index_sel, i); + // The lifetime of this pointer is tied to the NSString, as well as the current autorelease pool, which is why we heap-allocate the string below. + let utf_c_str: *const c_char = + MSG_SEND_NO_ARGUMENTS_RETURN_PTR(ns_string, utf8string_sel).cast(); let bytes = CStr::from_ptr(utf_c_str).to_bytes(); res.push(OsString::from(str::from_utf8(bytes).unwrap())) } From 341f0a1390e2549359f5c83d72674a84c69fe3d8 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Wed, 17 Jan 2024 14:33:24 +0300 Subject: [PATCH 04/17] revert temporary patch #108288 Signed-off-by: onur-ozkan --- src/bootstrap/src/core/build_steps/compile.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 190f0fe3cddcf..a6a5af22d2520 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1678,13 +1678,6 @@ impl Step for Assemble { // when not performing a full bootstrap). builder.ensure(Rustc::new(build_compiler, target_compiler.host)); - // FIXME: For now patch over problems noted in #90244 by early returning here, even though - // we've not properly assembled the target sysroot. A full fix is pending further investigation, - // for now full bootstrap usage is rare enough that this is OK. - if target_compiler.stage >= 3 && !builder.config.full_bootstrap { - return target_compiler; - } - for &backend in builder.config.rust_codegen_backends.iter() { if backend == "llvm" { continue; // Already built as part of rustc From 8a461aa8cb4dc0758d605eb9240f2bd563267a9b Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 18 Jan 2024 10:57:04 +0300 Subject: [PATCH 05/17] distribute actual stage of the compiled compiler By "actual" we refer to the uplifting logic where we may not compile the requested stage; instead, we uplift it from the previous stages. Which can lead to bootstrap failures in specific situations where we request stage X from other steps. However we may end up uplifting it from stage Y, causing the other stage to fail when attempting to link with stage X which was never actually built. Signed-off-by: onur-ozkan --- src/bootstrap/src/core/build_steps/compile.rs | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index a6a5af22d2520..f954f01fe4eda 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -803,7 +803,14 @@ impl Rustc { } impl Step for Rustc { - type Output = (); + // We return the stage of the "actual" compiler (not the uplifted one). + // + // By "actual" we refer to the uplifting logic where we may not compile the requested stage; + // instead, we uplift it from the previous stages. Which can lead to bootstrap failures in + // specific situations where we request stage X from other steps. However we may end up + // uplifting it from stage Y, causing the other stage to fail when attempting to link with + // stage X which was never actually built. + type Output = u32; const ONLY_HOSTS: bool = true; const DEFAULT: bool = false; @@ -834,7 +841,7 @@ impl Step for Rustc { /// This will build the compiler for a particular stage of the build using /// the `compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. - fn run(self, builder: &Builder<'_>) { + fn run(self, builder: &Builder<'_>) -> u32 { let compiler = self.compiler; let target = self.target; @@ -848,7 +855,7 @@ impl Step for Rustc { compiler, builder.config.ci_rustc_dev_contents(), ); - return; + return compiler.stage; } builder.ensure(Std::new(compiler, target)); @@ -857,7 +864,8 @@ impl Step for Rustc { builder.info("WARNING: Using a potentially old librustc. This may not behave well."); builder.info("WARNING: Use `--keep-stage-std` if you want to rebuild the compiler when it changes"); builder.ensure(RustcLink::from_rustc(self, compiler)); - return; + + return compiler.stage; } let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); @@ -880,7 +888,7 @@ impl Step for Rustc { }; builder.info(&msg); builder.ensure(RustcLink::from_rustc(self, compiler_to_use)); - return; + return compiler_to_use.stage; } // Ensure that build scripts and proc macros have a std / libproc_macro to link against. @@ -984,6 +992,8 @@ impl Step for Rustc { self, builder.compiler(compiler.stage, builder.config.build), )); + + compiler.stage } } @@ -1642,21 +1652,6 @@ impl Step for Assemble { return target_compiler; } - // Get the compiler that we'll use to bootstrap ourselves. - // - // Note that this is where the recursive nature of the bootstrap - // happens, as this will request the previous stage's compiler on - // downwards to stage 0. - // - // Also note that we're building a compiler for the host platform. We - // only assume that we can run `build` artifacts, which means that to - // produce some other architecture compiler we need to start from - // `build` to get there. - // - // FIXME: It may be faster if we build just a stage 1 compiler and then - // use that to bootstrap this compiler forward. - let build_compiler = builder.compiler(target_compiler.stage - 1, builder.config.build); - // If we're downloading a compiler from CI, we can use the same compiler for all stages other than 0. if builder.download_rustc() { let sysroot = @@ -1671,12 +1666,30 @@ impl Step for Assemble { return target_compiler; } + // Get the compiler that we'll use to bootstrap ourselves. + // + // Note that this is where the recursive nature of the bootstrap + // happens, as this will request the previous stage's compiler on + // downwards to stage 0. + // + // Also note that we're building a compiler for the host platform. We + // only assume that we can run `build` artifacts, which means that to + // produce some other architecture compiler we need to start from + // `build` to get there. + // + // FIXME: It may be faster if we build just a stage 1 compiler and then + // use that to bootstrap this compiler forward. + let mut build_compiler = builder.compiler(target_compiler.stage - 1, builder.config.build); + // Build the libraries for this compiler to link to (i.e., the libraries // it uses at runtime). NOTE: Crates the target compiler compiles don't // link to these. (FIXME: Is that correct? It seems to be correct most // of the time but I think we do link to these for stage2/bin compilers // when not performing a full bootstrap). - builder.ensure(Rustc::new(build_compiler, target_compiler.host)); + let actual_stage = builder.ensure(Rustc::new(build_compiler, target_compiler.host)); + // Current build_compiler.stage might be uplifted instead of being built; so update it + // to not fail while linking the artifacts. + build_compiler.stage = actual_stage; for &backend in builder.config.rust_codegen_backends.iter() { if backend == "llvm" { From 12ebc3dd966e1accc7d8e73aa4a4f3ea8d4bd9eb Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 18 Jan 2024 15:06:46 +0100 Subject: [PATCH 06/17] Add tests --- .../exhaustiveness-unreachable-pattern.rs | 18 +++++++++++ .../exhaustiveness-unreachable-pattern.stderr | 26 ++++++++++++++- .../unreachable.exh_pats.stderr | 32 +++++++++++++++++++ .../rfc-0000-never_patterns/unreachable.rs | 29 +++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr create mode 100644 tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs diff --git a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs index 20a8d7549961f..324ba54e0e763 100644 --- a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs +++ b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs @@ -160,3 +160,21 @@ fn main() { | (y, x) => {} //~ ERROR unreachable } } + +fn unreachable_in_param((_ | (_, _)): (bool, bool)) {} + +fn unreachable_in_binding() { + let bool_pair = (true, true); + let bool_option = Some(true); + + let (_ | (_, _)) = bool_pair; + for (_ | (_, _)) in [bool_pair] {} + //~^ ERROR unreachable + + let (Some(_) | Some(true)) = bool_option else { return }; + //~^ ERROR unreachable + if let Some(_) | Some(true) = bool_option {} + //~^ ERROR unreachable + while let Some(_) | Some(true) = bool_option {} + //~^ ERROR unreachable +} diff --git a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr index 3616dda99812f..d47645a4689b4 100644 --- a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr +++ b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr @@ -184,5 +184,29 @@ error: unreachable pattern LL | | (y, x) => {} | ^^^^^^ -error: aborting due to 29 previous errors +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:171:14 + | +LL | for (_ | (_, _)) in [bool_pair] {} + | ^^^^^^ + +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:174:20 + | +LL | let (Some(_) | Some(true)) = bool_option else { return }; + | ^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:176:22 + | +LL | if let Some(_) | Some(true) = bool_option {} + | ^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:178:25 + | +LL | while let Some(_) | Some(true) = bool_option {} + | ^^^^^^^^^^ + +error: aborting due to 33 previous errors diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr new file mode 100644 index 0000000000000..5a1d9ca50bdc5 --- /dev/null +++ b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr @@ -0,0 +1,32 @@ +error: unreachable pattern + --> $DIR/unreachable.rs:17:9 + | +LL | Err(!), + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/unreachable.rs:7:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/unreachable.rs:21:12 + | +LL | if let Err(!) = res_void {} + | ^^^^^^ + +error: unreachable pattern + --> $DIR/unreachable.rs:23:24 + | +LL | if let (Ok(true) | Err(!)) = res_void {} + | ^^^^^^ + +error: unreachable pattern + --> $DIR/unreachable.rs:25:23 + | +LL | for (Ok(mut _x) | Err(!)) in [res_void] {} + | ^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs new file mode 100644 index 0000000000000..7bc695fd962e6 --- /dev/null +++ b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs @@ -0,0 +1,29 @@ +// revisions: normal exh_pats +//[normal] check-pass +#![feature(never_patterns)] +#![allow(incomplete_features)] +#![cfg_attr(exh_pats, feature(exhaustive_patterns))] +#![allow(dead_code, unreachable_code)] +#![deny(unreachable_patterns)] + +#[derive(Copy, Clone)] +enum Void {} + +fn main() { + let res_void: Result = Ok(true); + + match res_void { + Ok(_x) => {} + Err(!), + //[exh_pats]~^ ERROR unreachable + } + let (Ok(_x) | Err(!)) = res_void; + if let Err(!) = res_void {} + //[exh_pats]~^ ERROR unreachable + if let (Ok(true) | Err(!)) = res_void {} + //[exh_pats]~^ ERROR unreachable + for (Ok(mut _x) | Err(!)) in [res_void] {} + //[exh_pats]~^ ERROR unreachable +} + +fn foo((Ok(_x) | Err(!)): Result) {} From 753680afe833ada2ffe4723519d127dc2d256e78 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 18 Jan 2024 14:45:41 +0100 Subject: [PATCH 07/17] Consistently set `MatchVisitor.error` on error --- .../src/thir/pattern/check_match.rs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index f6c5e4a5cd6e2..b2cb4240cf598 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1,9 +1,8 @@ use rustc_pattern_analysis::errors::Uncovered; use rustc_pattern_analysis::rustc::{ - Constructor, DeconstructedPat, RustcMatchCheckCtxt as MatchCheckCtxt, Usefulness, + Constructor, DeconstructedPat, MatchArm, RustcMatchCheckCtxt as MatchCheckCtxt, Usefulness, UsefulnessReport, WitnessPat, }; -use rustc_pattern_analysis::{analyze_match, MatchArm}; use crate::errors::*; @@ -386,6 +385,18 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { } } + fn analyze_patterns( + &mut self, + cx: &MatchCheckCtxt<'p, 'tcx>, + arms: &[MatchArm<'p, 'tcx>], + scrut_ty: Ty<'tcx>, + ) -> Result, ErrorGuaranteed> { + rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty).map_err(|err| { + self.error = Err(err); + err + }) + } + #[instrument(level = "trace", skip(self))] fn check_let(&mut self, pat: &'p Pat<'tcx>, scrutinee: Option, span: Span) { assert!(self.let_source != LetSource::None); @@ -431,14 +442,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { } } - let scrut_ty = scrut.ty; - let report = match analyze_match(&cx, &tarms, scrut_ty) { - Ok(report) => report, - Err(err) => { - self.error = Err(err); - return; - } - }; + let Ok(report) = self.analyze_patterns(&cx, &tarms, scrut.ty) else { return }; match source { // Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }` @@ -470,7 +474,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { ); } else { self.error = Err(report_non_exhaustive_match( - &cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span, + &cx, self.thir, scrut.ty, scrut.span, witnesses, arms, expr_span, )); } } @@ -552,7 +556,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { let cx = self.new_cx(refutability, None, scrut, pat.span); let pat = self.lower_pattern(&cx, pat)?; let arms = [MatchArm { pat, arm_data: self.lint_level, has_guard: false }]; - let report = analyze_match(&cx, &arms, pat.ty().inner())?; + let report = self.analyze_patterns(&cx, &arms, pat.ty().inner())?; Ok((cx, report)) } From 0a9bb9722907cbbd75a643fad1af3278517805fc Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 18 Jan 2024 14:59:48 +0100 Subject: [PATCH 08/17] Consistently warn unreachable subpatterns --- .../src/thir/pattern/check_match.rs | 68 ++++++++++--------- .../exhaustiveness-unreachable-pattern.rs | 2 + .../exhaustiveness-unreachable-pattern.stderr | 20 ++++-- .../unreachable.exh_pats.stderr | 20 ++++-- .../rfc-0000-never_patterns/unreachable.rs | 2 + tests/ui/unsafe/union_destructure.rs | 1 + 6 files changed, 74 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index b2cb4240cf598..dbd719db559a5 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -391,10 +391,26 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { arms: &[MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, ) -> Result, ErrorGuaranteed> { - rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty).map_err(|err| { - self.error = Err(err); - err - }) + let report = + rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty).map_err(|err| { + self.error = Err(err); + err + })?; + + // Warn unreachable subpatterns. + for (arm, is_useful) in report.arm_usefulness.iter() { + if let Usefulness::Useful(redundant_subpats) = is_useful + && !redundant_subpats.is_empty() + { + let mut redundant_subpats = redundant_subpats.clone(); + // Emit lints in the order in which they occur in the file. + redundant_subpats.sort_unstable_by_key(|pat| pat.data().unwrap().span); + for pat in redundant_subpats { + report_unreachable_pattern(cx, arm.arm_data, pat.data().unwrap().span, None) + } + } + } + Ok(report) } #[instrument(level = "trace", skip(self))] @@ -567,7 +583,6 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { ) -> Result { let (cx, report) = self.analyze_binding(pat, Refutable, scrut)?; // Report if the pattern is unreachable, which can only occur when the type is uninhabited. - // This also reports unreachable sub-patterns. report_arm_reachability(&cx, &report); // If the list of witnesses is empty, the match is exhaustive, i.e. the `if let` pattern is // irrefutable. @@ -837,39 +852,30 @@ fn report_irrefutable_let_patterns( } } +/// Report unreachable arms, if any. +fn report_unreachable_pattern<'p, 'tcx>( + cx: &MatchCheckCtxt<'p, 'tcx>, + hir_id: HirId, + span: Span, + catchall: Option, +) { + cx.tcx.emit_spanned_lint( + UNREACHABLE_PATTERNS, + hir_id, + span, + UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall }, + ); +} + /// Report unreachable arms, if any. fn report_arm_reachability<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, report: &UsefulnessReport<'p, 'tcx>, ) { - let report_unreachable_pattern = |span, hir_id, catchall: Option| { - cx.tcx.emit_spanned_lint( - UNREACHABLE_PATTERNS, - hir_id, - span, - UnreachablePattern { - span: if catchall.is_some() { Some(span) } else { None }, - catchall, - }, - ); - }; - let mut catchall = None; for (arm, is_useful) in report.arm_usefulness.iter() { - match is_useful { - Usefulness::Redundant => { - report_unreachable_pattern(arm.pat.data().unwrap().span, arm.arm_data, catchall) - } - Usefulness::Useful(redundant_subpats) if redundant_subpats.is_empty() => {} - // The arm is reachable, but contains redundant subpatterns (from or-patterns). - Usefulness::Useful(redundant_subpats) => { - let mut redundant_subpats = redundant_subpats.clone(); - // Emit lints in the order in which they occur in the file. - redundant_subpats.sort_unstable_by_key(|pat| pat.data().unwrap().span); - for pat in redundant_subpats { - report_unreachable_pattern(pat.data().unwrap().span, arm.arm_data, None); - } - } + if matches!(is_useful, Usefulness::Redundant) { + report_unreachable_pattern(cx, arm.arm_data, arm.pat.data().unwrap().span, catchall) } if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) { catchall = Some(arm.pat.data().unwrap().span); diff --git a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs index 324ba54e0e763..1ad335bf39481 100644 --- a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs +++ b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs @@ -162,12 +162,14 @@ fn main() { } fn unreachable_in_param((_ | (_, _)): (bool, bool)) {} +//~^ ERROR unreachable fn unreachable_in_binding() { let bool_pair = (true, true); let bool_option = Some(true); let (_ | (_, _)) = bool_pair; + //~^ ERROR unreachable for (_ | (_, _)) in [bool_pair] {} //~^ ERROR unreachable diff --git a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr index d47645a4689b4..336530d1b320a 100644 --- a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr +++ b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr @@ -184,29 +184,41 @@ error: unreachable pattern LL | | (y, x) => {} | ^^^^^^ +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:164:30 + | +LL | fn unreachable_in_param((_ | (_, _)): (bool, bool)) {} + | ^^^^^^ + error: unreachable pattern --> $DIR/exhaustiveness-unreachable-pattern.rs:171:14 | +LL | let (_ | (_, _)) = bool_pair; + | ^^^^^^ + +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:173:14 + | LL | for (_ | (_, _)) in [bool_pair] {} | ^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:174:20 + --> $DIR/exhaustiveness-unreachable-pattern.rs:176:20 | LL | let (Some(_) | Some(true)) = bool_option else { return }; | ^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:176:22 + --> $DIR/exhaustiveness-unreachable-pattern.rs:178:22 | LL | if let Some(_) | Some(true) = bool_option {} | ^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:178:25 + --> $DIR/exhaustiveness-unreachable-pattern.rs:180:25 | LL | while let Some(_) | Some(true) = bool_option {} | ^^^^^^^^^^ -error: aborting due to 33 previous errors +error: aborting due to 35 previous errors diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr index 5a1d9ca50bdc5..fe2a72d2a3180 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr +++ b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr @@ -11,22 +11,34 @@ LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/unreachable.rs:21:12 + --> $DIR/unreachable.rs:20:19 + | +LL | let (Ok(_x) | Err(!)) = res_void; + | ^^^^^^ + +error: unreachable pattern + --> $DIR/unreachable.rs:22:12 | LL | if let Err(!) = res_void {} | ^^^^^^ error: unreachable pattern - --> $DIR/unreachable.rs:23:24 + --> $DIR/unreachable.rs:24:24 | LL | if let (Ok(true) | Err(!)) = res_void {} | ^^^^^^ error: unreachable pattern - --> $DIR/unreachable.rs:25:23 + --> $DIR/unreachable.rs:26:23 | LL | for (Ok(mut _x) | Err(!)) in [res_void] {} | ^^^^^^ -error: aborting due to 4 previous errors +error: unreachable pattern + --> $DIR/unreachable.rs:30:18 + | +LL | fn foo((Ok(_x) | Err(!)): Result) {} + | ^^^^^^ + +error: aborting due to 6 previous errors diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs index 7bc695fd962e6..df8e22abf6232 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs +++ b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs @@ -18,6 +18,7 @@ fn main() { //[exh_pats]~^ ERROR unreachable } let (Ok(_x) | Err(!)) = res_void; + //[exh_pats]~^ ERROR unreachable if let Err(!) = res_void {} //[exh_pats]~^ ERROR unreachable if let (Ok(true) | Err(!)) = res_void {} @@ -27,3 +28,4 @@ fn main() { } fn foo((Ok(_x) | Err(!)): Result) {} +//[exh_pats]~^ ERROR unreachable diff --git a/tests/ui/unsafe/union_destructure.rs b/tests/ui/unsafe/union_destructure.rs index d0cf8640eaa22..bb75dbf0997ce 100644 --- a/tests/ui/unsafe/union_destructure.rs +++ b/tests/ui/unsafe/union_destructure.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unreachable_patterns)] #[derive(Copy, Clone)] #[allow(dead_code)] From f26f52c42bdce2bf6db4a28ff33608ba3890add2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 19 Jan 2024 19:47:00 +0000 Subject: [PATCH 09/17] Validate AggregateKind types in MIR --- .../src/transform/validate.rs | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 9c2f336e9128c..5564902396eda 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -796,7 +796,67 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { }; } match rvalue { - Rvalue::Use(_) | Rvalue::CopyForDeref(_) | Rvalue::Aggregate(..) => {} + Rvalue::Use(_) | Rvalue::CopyForDeref(_) => {} + Rvalue::Aggregate(kind, fields) => match **kind { + AggregateKind::Tuple => {} + AggregateKind::Array(dest) => { + for src in fields { + if !self.mir_assign_valid_types(src.ty(self.body, self.tcx), dest) { + self.fail(location, "array field has the wrong type"); + } + } + } + AggregateKind::Adt(def_id, idx, args, _, Some(field)) => { + let adt_def = self.tcx.adt_def(def_id); + assert!(adt_def.is_union()); + assert_eq!(idx, FIRST_VARIANT); + let dest = adt_def.non_enum_variant().fields[field].ty(self.tcx, args); + if fields.len() != 1 { + self.fail(location, "unions should have one initialized field"); + } + if !self.mir_assign_valid_types(fields.raw[0].ty(self.body, self.tcx), dest) { + self.fail(location, "union field has the wrong type"); + } + } + AggregateKind::Adt(def_id, idx, args, _, None) => { + let adt_def = self.tcx.adt_def(def_id); + assert!(!adt_def.is_union()); + let variant = &adt_def.variants()[idx]; + if variant.fields.len() != fields.len() { + self.fail(location, "adt has the wrong number of initialized fields"); + } + for (src, dest) in std::iter::zip(fields, &variant.fields) { + if !self.mir_assign_valid_types( + src.ty(self.body, self.tcx), + dest.ty(self.tcx, args), + ) { + self.fail(location, "adt field has the wrong type"); + } + } + } + AggregateKind::Closure(_, args) => { + let upvars = args.as_closure().upvar_tys(); + if upvars.len() != fields.len() { + self.fail(location, "closure has the wrong number of initialized fields"); + } + for (src, dest) in std::iter::zip(fields, upvars) { + if !self.mir_assign_valid_types(src.ty(self.body, self.tcx), dest) { + self.fail(location, "closure field has the wrong type"); + } + } + } + AggregateKind::Coroutine(_, args) => { + let upvars = args.as_coroutine().upvar_tys(); + if upvars.len() != fields.len() { + self.fail(location, "coroutine has the wrong number of initialized fields"); + } + for (src, dest) in std::iter::zip(fields, upvars) { + if !self.mir_assign_valid_types(src.ty(self.body, self.tcx), dest) { + self.fail(location, "coroutine field has the wrong type"); + } + } + } + }, Rvalue::Ref(_, BorrowKind::Fake, _) => { if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( From fc75a4e14607a9b6445fd0c6685fb6999fa69de8 Mon Sep 17 00:00:00 2001 From: Nikolai Vazquez Date: Tue, 3 Oct 2023 14:44:41 -0400 Subject: [PATCH 10/17] Allow any expression blocks in `thread_local!` --- library/std/src/thread/local.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 338567777f753..4c5e0e9139458 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -166,7 +166,10 @@ impl fmt::Debug for LocalKey { /// ``` /// use std::cell::Cell; /// thread_local! { -/// pub static FOO: Cell = const { Cell::new(1) }; +/// pub static FOO: Cell = const { +/// let value = 1; +/// Cell::new(value) +/// }; /// } /// /// assert_eq!(FOO.get(), 1); @@ -186,12 +189,12 @@ macro_rules! thread_local { // empty (base case for the recursion) () => {}; - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }; $($rest:tt)*) => ( + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const $init:block; $($rest:tt)*) => ( $crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, const $init); $crate::thread_local!($($rest)*); ); - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }) => ( + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const $init:block) => ( $crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, const $init); ); From f52b88e91f2d185a8be2bf4243827f7b720cd513 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 20 Jan 2024 18:39:48 -0800 Subject: [PATCH 11/17] Revert example change from PR 116392 --- library/std/src/thread/local.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 4c5e0e9139458..2e13f433dcffb 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -166,10 +166,7 @@ impl fmt::Debug for LocalKey { /// ``` /// use std::cell::Cell; /// thread_local! { -/// pub static FOO: Cell = const { -/// let value = 1; -/// Cell::new(value) -/// }; +/// pub static FOO: Cell = const { Cell::new(1) }; /// } /// /// assert_eq!(FOO.get(), 1); From c43344e839906624a388847906f6a9b27dcd35c4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 20 Jan 2024 18:48:41 -0800 Subject: [PATCH 12/17] Add test of thread_local containing multiline const block Before making thread_local accept statements inside the const block, this test would fail to compile as follows: error: no rules expected the token `let` --> library/std/tests/thread.rs:26:13 | 26 | let value = 1; | ^^^ no rules expected this token in macro call | note: while trying to match meta-variable `$init:expr` --> library/std/src/thread/local.rs:189:69 | 189 | ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }; $($rest:tt)*) => ( | ^^^^^^^^^^ --- library/std/tests/thread.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/library/std/tests/thread.rs b/library/std/tests/thread.rs index 754b264c6ad93..4ce81f2846ea9 100644 --- a/library/std/tests/thread.rs +++ b/library/std/tests/thread.rs @@ -1,3 +1,4 @@ +use std::cell::{Cell, RefCell}; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; @@ -14,3 +15,24 @@ fn sleep() { thread::sleep(Duration::from_millis(100)); assert_eq!(*finished.lock().unwrap(), false); } + +#[test] +fn thread_local_containing_const_statements() { + // This exercises the `const $init:block` cases of the thread_local macro. + // Despite overlapping with expression syntax, the `const { ... }` is not + // parsed as `$init:expr`. + thread_local! { + static CELL: Cell = const { + let value = 1; + Cell::new(value) + }; + + static REFCELL: RefCell = const { + let value = 1; + RefCell::new(value) + }; + } + + assert_eq!(CELL.get(), 1); + assert_eq!(REFCELL.take(), 1); +} From b58a8a98cdee84a9636c08877f623b97033c7dda Mon Sep 17 00:00:00 2001 From: trevyn <230691+trevyn@users.noreply.github.com> Date: Sat, 20 Jan 2024 20:12:54 +0400 Subject: [PATCH 13/17] `maybe_lint_impl_trait`: separate `is_downgradable` from `is_object_safe` --- .../rustc_hir_analysis/src/astconv/lint.rs | 16 ++-- ...-trait-should-use-self-2021-without-dyn.rs | 6 +- ...it-should-use-self-2021-without-dyn.stderr | 96 ++++++++++--------- 3 files changed, 65 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs index b5f42e98127ee..27dc088d5dd8c 100644 --- a/compiler/rustc_hir_analysis/src/astconv/lint.rs +++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs @@ -94,15 +94,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return false; }; let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())]; + let mut is_downgradable = true; let is_object_safe = match self_ty.kind { hir::TyKind::TraitObject(objects, ..) => { objects.iter().all(|o| match o.trait_ref.path.res { - Res::Def(DefKind::Trait, id) if Some(id) == owner => { - // When we're dealing with a recursive trait, we don't want to downgrade - // the error, so we consider them to be object safe always. (#119652) - true + Res::Def(DefKind::Trait, id) => { + if Some(id) == owner { + // For recursive traits, don't downgrade the error. (#119652) + is_downgradable = false; + } + tcx.check_is_object_safe(id) } - Res::Def(DefKind::Trait, id) => tcx.check_is_object_safe(id), _ => false, }) } @@ -130,7 +132,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ], Applicability::MachineApplicable, ); - } else if diag.is_error() { + } else if diag.is_error() && is_downgradable { // We'll emit the object safety error already, with a structured suggestion. diag.downgrade_to_delayed_bug(); } @@ -156,7 +158,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } if !is_object_safe { diag.note(format!("`{trait_name}` it is not object safe, so it can't be `dyn`")); - if diag.is_error() { + if diag.is_error() && is_downgradable { // We'll emit the object safety error already, with a structured suggestion. diag.downgrade_to_delayed_bug(); } diff --git a/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.rs b/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.rs index f48c3d124ddac..dbdfde6dd5061 100644 --- a/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.rs +++ b/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.rs @@ -8,16 +8,18 @@ trait A: Sized { //~| ERROR the trait `A` cannot be made into an object } trait B { - fn f(a: B) -> B; + fn f(b: B) -> B; //~^ ERROR trait objects must include the `dyn` keyword //~| ERROR trait objects must include the `dyn` keyword //~| ERROR associated item referring to unboxed trait object for its own trait //~| ERROR the trait `B` cannot be made into an object } trait C { - fn f(&self, a: C) -> C; + fn f(&self, c: C) -> C; //~^ ERROR trait objects must include the `dyn` keyword //~| ERROR trait objects must include the `dyn` keyword + //~| ERROR associated item referring to unboxed trait object for its own trait + //~| ERROR the trait `C` cannot be made into an object } fn main() {} diff --git a/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.stderr b/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.stderr index 73d5a24f83137..60eb72ab4f768 100644 --- a/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.stderr +++ b/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.stderr @@ -30,18 +30,18 @@ error: associated item referring to unboxed trait object for its own trait | LL | trait B { | - in this trait -LL | fn f(a: B) -> B; +LL | fn f(b: B) -> B; | ^ ^ | help: you might have meant to use `Self` to refer to the implementing type | -LL | fn f(a: Self) -> Self; +LL | fn f(b: Self) -> Self; | ~~~~ ~~~~ error[E0038]: the trait `B` cannot be made into an object --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:11:13 | -LL | fn f(a: B) -> B; +LL | fn f(b: B) -> B; | ^ `B` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit @@ -49,23 +49,53 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all | LL | trait B { | - this trait cannot be made into an object... -LL | fn f(a: B) -> B; +LL | fn f(b: B) -> B; | ^ ...because associated function `f` has no `self` parameter help: consider turning `f` into a method by giving it a `&self` argument | -LL | fn f(&self, a: B) -> B; +LL | fn f(&self, b: B) -> B; | ++++++ help: alternatively, consider constraining `f` so it does not apply to trait objects | -LL | fn f(a: B) -> B where Self: Sized; +LL | fn f(b: B) -> B where Self: Sized; | +++++++++++++++++ +error: associated item referring to unboxed trait object for its own trait + --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:18:20 + | +LL | trait C { + | - in this trait +LL | fn f(&self, c: C) -> C; + | ^ ^ + | +help: you might have meant to use `Self` to refer to the implementing type + | +LL | fn f(&self, c: Self) -> Self; + | ~~~~ ~~~~ + +error[E0038]: the trait `C` cannot be made into an object + --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:18:20 + | +LL | fn f(&self, c: C) -> C; + | ----- ^ `C` cannot be made into an object + | | + | help: consider changing method `f`'s `self` parameter to be `&self` (notice the capitalization): `&Self` + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:18:10 + | +LL | trait C { + | - this trait cannot be made into an object... +LL | fn f(&self, c: C) -> C; + | ^^^^^ ...because method `f`'s `self` parameter cannot be dispatched on + error[E0782]: trait objects must include the `dyn` keyword --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:4:13 | LL | fn f(a: A) -> A; | ^ | + = note: `A` it is not object safe, so it can't be `dyn` help: use a new generic type parameter, constrained by `A` | LL | fn f(a: T) -> A; @@ -74,10 +104,6 @@ help: you can also use an opaque type, but users won't be able to specify the ty | LL | fn f(a: impl A) -> A; | ++++ -help: alternatively, use a trait object to accept any type that implements `A`, accessing its methods at runtime using dynamic dispatch - | -LL | fn f(a: &dyn A) -> A; - | ++++ error[E0782]: trait objects must include the `dyn` keyword --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:4:19 @@ -85,84 +111,66 @@ error[E0782]: trait objects must include the `dyn` keyword LL | fn f(a: A) -> A; | ^ | -help: use `impl A` to return an opaque type, as long as you return a single underlying type +help: `A` is not object safe, use `impl A` to return an opaque type, as long as you return a single underlying type | LL | fn f(a: A) -> impl A; | ++++ -help: alternatively, you can return an owned trait object - | -LL | fn f(a: A) -> Box; - | +++++++ + error[E0782]: trait objects must include the `dyn` keyword --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:11:13 | -LL | fn f(a: B) -> B; +LL | fn f(b: B) -> B; | ^ | + = note: `B` it is not object safe, so it can't be `dyn` help: use a new generic type parameter, constrained by `B` | -LL | fn f(a: T) -> B; +LL | fn f(b: T) -> B; | ++++++ ~ help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | -LL | fn f(a: impl B) -> B; - | ++++ -help: alternatively, use a trait object to accept any type that implements `B`, accessing its methods at runtime using dynamic dispatch - | -LL | fn f(a: &dyn B) -> B; +LL | fn f(b: impl B) -> B; | ++++ error[E0782]: trait objects must include the `dyn` keyword --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:11:19 | -LL | fn f(a: B) -> B; +LL | fn f(b: B) -> B; | ^ | -help: use `impl B` to return an opaque type, as long as you return a single underlying type +help: `B` is not object safe, use `impl B` to return an opaque type, as long as you return a single underlying type | -LL | fn f(a: B) -> impl B; +LL | fn f(b: B) -> impl B; | ++++ -help: alternatively, you can return an owned trait object - | -LL | fn f(a: B) -> Box; - | +++++++ + error[E0782]: trait objects must include the `dyn` keyword --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:18:20 | -LL | fn f(&self, a: C) -> C; +LL | fn f(&self, c: C) -> C; | ^ | + = note: `C` it is not object safe, so it can't be `dyn` help: use a new generic type parameter, constrained by `C` | -LL | fn f(&self, a: T) -> C; +LL | fn f(&self, c: T) -> C; | ++++++ ~ help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | -LL | fn f(&self, a: impl C) -> C; - | ++++ -help: alternatively, use a trait object to accept any type that implements `C`, accessing its methods at runtime using dynamic dispatch - | -LL | fn f(&self, a: &dyn C) -> C; +LL | fn f(&self, c: impl C) -> C; | ++++ error[E0782]: trait objects must include the `dyn` keyword --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:18:26 | -LL | fn f(&self, a: C) -> C; +LL | fn f(&self, c: C) -> C; | ^ | -help: use `impl C` to return an opaque type, as long as you return a single underlying type +help: `C` is not object safe, use `impl C` to return an opaque type, as long as you return a single underlying type | -LL | fn f(&self, a: C) -> impl C; +LL | fn f(&self, c: C) -> impl C; | ++++ -help: alternatively, you can return an owned trait object - | -LL | fn f(&self, a: C) -> Box; - | +++++++ + -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors Some errors have detailed explanations: E0038, E0782. For more information about an error, try `rustc --explain E0038`. From 981e8b46c5bbb78bf39207bbfb8fe6f8d6ba853e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 22 Jan 2024 01:18:16 +0000 Subject: [PATCH 14/17] Check that a token can begin a nonterminal kind before parsing it as a macro arg in rustfmt --- src/tools/rustfmt/src/parse/macros/mod.rs | 41 +++++++++++-------- .../tests/source/macros/rewrite-const-item.rs | 1 + .../tests/target/macros/rewrite-const-item.rs | 3 ++ 3 files changed, 28 insertions(+), 17 deletions(-) create mode 100644 src/tools/rustfmt/tests/source/macros/rewrite-const-item.rs create mode 100644 src/tools/rustfmt/tests/target/macros/rewrite-const-item.rs diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index 2dd2622174f81..36e3972a4633c 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -1,7 +1,7 @@ use rustc_ast::token::{Delimiter, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{ast, ptr}; -use rustc_parse::parser::{ForceCollect, Parser}; +use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_parse::{stream_to_parser, MACRO_ARGUMENTS}; use rustc_session::parse::ParseSess; use rustc_span::symbol::{self, kw}; @@ -24,45 +24,52 @@ fn build_parser<'a>(context: &RewriteContext<'a>, tokens: TokenStream) -> Parser fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { macro_rules! parse_macro_arg { - ($macro_arg:ident, $parser:expr, $f:expr) => { + ($macro_arg:ident, $can_begin:expr, $try_parse:expr, $then:expr) => { let mut cloned_parser = (*parser).clone(); - match $parser(&mut cloned_parser) { - Ok(x) => { - if parser.sess.dcx.has_errors().is_some() { + if $can_begin(&mut cloned_parser) { + match $try_parse(&mut cloned_parser) { + Ok(x) => { + if parser.sess.dcx.has_errors().is_some() { + parser.sess.dcx.reset_err_count(); + } else { + // Parsing succeeded. + *parser = cloned_parser; + return Some(MacroArg::$macro_arg($then(x)?)); + } + } + Err(e) => { + e.cancel(); parser.sess.dcx.reset_err_count(); - } else { - // Parsing succeeded. - *parser = cloned_parser; - return Some(MacroArg::$macro_arg($f(x)?)); } } - Err(e) => { - e.cancel(); - parser.sess.dcx.reset_err_count(); - } } }; } parse_macro_arg!( Expr, - |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_expr(), + |parser: &mut Parser<'b>| parser.token.can_begin_expr(), + |parser: &mut Parser<'b>| parser.parse_expr(), |x: ptr::P| Some(x) ); parse_macro_arg!( Ty, - |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_ty(), + |parser: &mut Parser<'b>| parser.token.can_begin_type(), + |parser: &mut Parser<'b>| parser.parse_ty(), |x: ptr::P| Some(x) ); parse_macro_arg!( Pat, - |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None, None), + // FIXME: This isn't right + |_| true, + |parser: &mut Parser<'b>| parser.parse_pat_no_top_alt(None, None), |x: ptr::P| Some(x) ); // `parse_item` returns `Option>`. parse_macro_arg!( Item, - |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_item(ForceCollect::No), + |_| true, + |parser: &mut Parser<'b>| parser.parse_item(ForceCollect::No), |x: Option>| x ); diff --git a/src/tools/rustfmt/tests/source/macros/rewrite-const-item.rs b/src/tools/rustfmt/tests/source/macros/rewrite-const-item.rs new file mode 100644 index 0000000000000..3db2c26ab5aa7 --- /dev/null +++ b/src/tools/rustfmt/tests/source/macros/rewrite-const-item.rs @@ -0,0 +1 @@ +m!(const N: usize = 0;); diff --git a/src/tools/rustfmt/tests/target/macros/rewrite-const-item.rs b/src/tools/rustfmt/tests/target/macros/rewrite-const-item.rs new file mode 100644 index 0000000000000..f7ebaf7827726 --- /dev/null +++ b/src/tools/rustfmt/tests/target/macros/rewrite-const-item.rs @@ -0,0 +1,3 @@ +m!( + const N: usize = 0; +); From b93ae21441eeb466f300efff41f97f1c8b9e0508 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 22 Jan 2024 00:56:58 +0000 Subject: [PATCH 15/17] Do not eagerly recover malformed AST in rustfmt --- src/tools/rustfmt/src/parse/macros/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index 36e3972a4633c..c6ee96ebb8aa6 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -15,7 +15,7 @@ pub(crate) mod cfg_if; pub(crate) mod lazy_static; fn build_stream_parser<'a>(sess: &'a ParseSess, tokens: TokenStream) -> Parser<'a> { - stream_to_parser(sess, tokens, MACRO_ARGUMENTS) + stream_to_parser(sess, tokens, MACRO_ARGUMENTS).recovery(Recovery::Forbidden) } fn build_parser<'a>(context: &RewriteContext<'a>, tokens: TokenStream) -> Parser<'a> { From 5297433747e5cd2c3794fd9eb88917797e5758ae Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 22 Jan 2024 01:55:49 +0000 Subject: [PATCH 16/17] Actually, just use nonterminal_may_begin_with --- src/tools/rustfmt/src/parse/macros/mod.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index c6ee96ebb8aa6..a9f9ea1826ae5 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -1,4 +1,4 @@ -use rustc_ast::token::{Delimiter, TokenKind}; +use rustc_ast::token::{Delimiter, NonterminalKind, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{ast, ptr}; use rustc_parse::parser::{ForceCollect, Parser, Recovery}; @@ -24,9 +24,9 @@ fn build_parser<'a>(context: &RewriteContext<'a>, tokens: TokenStream) -> Parser fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { macro_rules! parse_macro_arg { - ($macro_arg:ident, $can_begin:expr, $try_parse:expr, $then:expr) => { + ($macro_arg:ident, $nt_kind:expr, $try_parse:expr, $then:expr) => { let mut cloned_parser = (*parser).clone(); - if $can_begin(&mut cloned_parser) { + if Parser::nonterminal_may_begin_with($nt_kind, &cloned_parser.token) { match $try_parse(&mut cloned_parser) { Ok(x) => { if parser.sess.dcx.has_errors().is_some() { @@ -48,27 +48,26 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { parse_macro_arg!( Expr, - |parser: &mut Parser<'b>| parser.token.can_begin_expr(), + NonterminalKind::Expr, |parser: &mut Parser<'b>| parser.parse_expr(), |x: ptr::P| Some(x) ); parse_macro_arg!( Ty, - |parser: &mut Parser<'b>| parser.token.can_begin_type(), + NonterminalKind::Ty, |parser: &mut Parser<'b>| parser.parse_ty(), |x: ptr::P| Some(x) ); parse_macro_arg!( Pat, - // FIXME: This isn't right - |_| true, + NonterminalKind::PatParam { inferred: false }, |parser: &mut Parser<'b>| parser.parse_pat_no_top_alt(None, None), |x: ptr::P| Some(x) ); // `parse_item` returns `Option>`. parse_macro_arg!( Item, - |_| true, + NonterminalKind::Item, |parser: &mut Parser<'b>| parser.parse_item(ForceCollect::No), |x: Option>| x ); From 9454b51b0525632a6459d5802c7dbafc95a9a533 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 17 Jan 2024 14:57:48 +0000 Subject: [PATCH 17/17] Make generic const type mismatches not hide trait impls from the trait solver --- .../rustc_infer/src/infer/relate/combine.rs | 34 ++++++------------- .../ui/const-generics/bad-subst-const-kind.rs | 1 + .../bad-subst-const-kind.stderr | 16 ++++++++- .../generic_const_exprs/type_mismatch.rs | 3 ++ .../generic_const_exprs/type_mismatch.stderr | 33 ++++++++++++++++-- .../bad-const-wf-doesnt-specialize.rs | 1 + .../bad-const-wf-doesnt-specialize.stderr | 23 ++++++++++--- 7 files changed, 80 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index 9e1dab12b4d43..1c120646f1f90 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -165,9 +165,9 @@ impl<'tcx> InferCtxt<'tcx> { // // This probe is probably not strictly necessary but it seems better to be safe and not accidentally find // ourselves with a check to find bugs being required for code to compile because it made inference progress. - let compatible_types = self.probe(|_| { + self.probe(|_| { if a.ty() == b.ty() { - return Ok(()); + return; } // We don't have access to trait solving machinery in `rustc_infer` so the logic for determining if the @@ -177,32 +177,18 @@ impl<'tcx> InferCtxt<'tcx> { relation.param_env().and((a.ty(), b.ty())), &mut OriginalQueryValues::default(), ); - self.tcx.check_tys_might_be_eq(canonical).map_err(|_| { + self.tcx.check_tys_might_be_eq(canonical).unwrap_or_else(|_| { + // The error will only be reported later. If we emit an ErrorGuaranteed + // here, then we will never get to the code that actually emits the error. self.tcx.dcx().delayed_bug(format!( "cannot relate consts of different types (a={a:?}, b={b:?})", - )) - }) + )); + // We treat these constants as if they were of the same type, so that any + // such constants being used in impls make these impls match barring other mismatches. + // This helps with diagnostics down the road. + }); }); - // If the consts have differing types, just bail with a const error with - // the expected const's type. Specifically, we don't want const infer vars - // to do any type shapeshifting before and after resolution. - if let Err(guar) = compatible_types { - // HACK: equating both sides with `[const error]` eagerly prevents us - // from leaving unconstrained inference vars during things like impl - // matching in the solver. - let a_error = ty::Const::new_error(self.tcx, guar, a.ty()); - if let ty::ConstKind::Infer(InferConst::Var(vid)) = a.kind() { - return self.unify_const_variable(vid, a_error, relation.param_env()); - } - let b_error = ty::Const::new_error(self.tcx, guar, b.ty()); - if let ty::ConstKind::Infer(InferConst::Var(vid)) = b.kind() { - return self.unify_const_variable(vid, b_error, relation.param_env()); - } - - return Ok(if relation.a_is_expected() { a_error } else { b_error }); - } - match (a.kind(), b.kind()) { ( ty::ConstKind::Infer(InferConst::Var(a_vid)), diff --git a/tests/ui/const-generics/bad-subst-const-kind.rs b/tests/ui/const-generics/bad-subst-const-kind.rs index e13dfbacd2422..376b18c15b8ae 100644 --- a/tests/ui/const-generics/bad-subst-const-kind.rs +++ b/tests/ui/const-generics/bad-subst-const-kind.rs @@ -11,3 +11,4 @@ impl Q for [u8; N] { } pub fn test() -> [u8; <[u8; 13] as Q>::ASSOC] { todo!() } +//~^ ERROR: the constant `13` is not of type `u64` diff --git a/tests/ui/const-generics/bad-subst-const-kind.stderr b/tests/ui/const-generics/bad-subst-const-kind.stderr index 6f5bc567c378b..c04a05573bed0 100644 --- a/tests/ui/const-generics/bad-subst-const-kind.stderr +++ b/tests/ui/const-generics/bad-subst-const-kind.stderr @@ -1,9 +1,23 @@ +error: the constant `13` is not of type `u64` + --> $DIR/bad-subst-const-kind.rs:13:24 + | +LL | pub fn test() -> [u8; <[u8; 13] as Q>::ASSOC] { todo!() } + | ^^^^^^^^ expected `u64`, found `usize` + | +note: required for `[u8; 13]` to implement `Q` + --> $DIR/bad-subst-const-kind.rs:8:20 + | +LL | impl Q for [u8; N] { + | ------------ ^ ^^^^^^^ + | | + | unsatisfied trait bound introduced here + error[E0308]: mismatched types --> $DIR/bad-subst-const-kind.rs:8:31 | LL | impl Q for [u8; N] { | ^ expected `usize`, found `u64` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/generic_const_exprs/type_mismatch.rs b/tests/ui/const-generics/generic_const_exprs/type_mismatch.rs index 5813f09818410..6b0d9e047dbc3 100644 --- a/tests/ui/const-generics/generic_const_exprs/type_mismatch.rs +++ b/tests/ui/const-generics/generic_const_exprs/type_mismatch.rs @@ -7,7 +7,10 @@ trait Q { impl Q for [u8; N] {} //~^ ERROR not all trait items implemented +//~| ERROR mismatched types pub fn q_user() -> [u8; <[u8; 13] as Q>::ASSOC] {} +//~^ ERROR the constant `13` is not of type `u64` +//~| ERROR mismatched types pub fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr b/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr index c73d1022ed312..bb6d650b7ab27 100644 --- a/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr +++ b/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr @@ -7,6 +7,35 @@ LL | const ASSOC: usize; LL | impl Q for [u8; N] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `ASSOC` in implementation -error: aborting due to 1 previous error +error: the constant `13` is not of type `u64` + --> $DIR/type_mismatch.rs:12:26 + | +LL | pub fn q_user() -> [u8; <[u8; 13] as Q>::ASSOC] {} + | ^^^^^^^^ expected `u64`, found `usize` + | +note: required for `[u8; 13]` to implement `Q` + --> $DIR/type_mismatch.rs:8:20 + | +LL | impl Q for [u8; N] {} + | ------------ ^ ^^^^^^^ + | | + | unsatisfied trait bound introduced here + +error[E0308]: mismatched types + --> $DIR/type_mismatch.rs:12:20 + | +LL | pub fn q_user() -> [u8; <[u8; 13] as Q>::ASSOC] {} + | ------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `[u8; <[u8; 13] as Q>::ASSOC]`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + +error[E0308]: mismatched types + --> $DIR/type_mismatch.rs:8:31 + | +LL | impl Q for [u8; N] {} + | ^ expected `usize`, found `u64` + +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0046`. +Some errors have detailed explanations: E0046, E0308. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs index 5fd7c647c2531..8ceecd801d3a7 100644 --- a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs +++ b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs @@ -8,5 +8,6 @@ struct S; impl Copy for S {} //~^ ERROR the constant `N` is not of type `usize` impl Copy for S {} +//~^ ERROR: conflicting implementations of trait `Copy` for type `S<_>` fn main() {} diff --git a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr index def4a413af111..3268a8767339f 100644 --- a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr +++ b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr @@ -1,14 +1,29 @@ +error[E0119]: conflicting implementations of trait `Copy` for type `S<_>` + --> $DIR/bad-const-wf-doesnt-specialize.rs:10:1 + | +LL | impl Copy for S {} + | -------------------------------- first implementation here +LL | +LL | impl Copy for S {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `S<_>` + error: the constant `N` is not of type `usize` --> $DIR/bad-const-wf-doesnt-specialize.rs:8:29 | LL | impl Copy for S {} | ^^^^ expected `usize`, found `i32` | -note: required by a bound in `S` - --> $DIR/bad-const-wf-doesnt-specialize.rs:6:10 +note: required for `S` to implement `Clone` + --> $DIR/bad-const-wf-doesnt-specialize.rs:5:10 | +LL | #[derive(Clone)] + | ^^^^^ LL | struct S; - | ^^^^^^^^^^^^^^ required by this bound in `S` + | ----- unsatisfied trait bound introduced in this `derive` macro +note: required by a bound in `Copy` + --> $SRC_DIR/core/src/marker.rs:LL:COL + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 1 previous error +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0119`.