From 7c889620180c49eeaa2243d1b0548974475c599c Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sun, 16 Aug 2026 10:01:47 +0000 Subject: [PATCH 1/9] add crashtests, remove unused aux files --- tests/crashes/153005.rs | 15 +++++++++++++++ tests/crashes/153362.rs | 6 ++++++ tests/crashes/153375.rs | 15 +++++++++++++++ tests/crashes/153947.rs | 10 ++++++++++ tests/crashes/154296.rs | 12 ++++++++++++ tests/crashes/154779.rs | 4 ++++ tests/crashes/154782.rs | 9 +++++++++ tests/crashes/154871.rs | 7 +++++++ tests/crashes/auxiliary/aux132985.rs | 6 ------ tests/crashes/auxiliary/aux153375.rs | 6 ++++++ .../crashes/auxiliary/overlapping_spans_helper.rs | 15 --------------- 11 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 tests/crashes/153005.rs create mode 100644 tests/crashes/153362.rs create mode 100644 tests/crashes/153375.rs create mode 100644 tests/crashes/153947.rs create mode 100644 tests/crashes/154296.rs create mode 100644 tests/crashes/154779.rs create mode 100644 tests/crashes/154782.rs create mode 100644 tests/crashes/154871.rs delete mode 100644 tests/crashes/auxiliary/aux132985.rs create mode 100644 tests/crashes/auxiliary/aux153375.rs delete mode 100644 tests/crashes/auxiliary/overlapping_spans_helper.rs diff --git a/tests/crashes/153005.rs b/tests/crashes/153005.rs new file mode 100644 index 0000000000000..44b4e029958cd --- /dev/null +++ b/tests/crashes/153005.rs @@ -0,0 +1,15 @@ +//@ known-bug: #153005 +#![feature(non_lifetime_binders)] +#![feature(derive_coerce_pointee)] + +#[derive(core::marker::CoercePointee)] +#[repr(transparent)] +struct _Ptr5<'a, #[pointee] T: ?Sized, X> +where + for V: Sized, +{ + data: &'a T, + x: core::marker::PhantomData, +} + +fn main() {} diff --git a/tests/crashes/153362.rs b/tests/crashes/153362.rs new file mode 100644 index 0000000000000..0a8e4ddae6283 --- /dev/null +++ b/tests/crashes/153362.rs @@ -0,0 +1,6 @@ +//@ known-bug: #153362 +struct ThinDst { + b: unsafe<> (), +} + +const C1: &ThinDst = unsafe { std::mem::transmute(b"d".as_ptr()) }; diff --git a/tests/crashes/153375.rs b/tests/crashes/153375.rs new file mode 100644 index 0000000000000..46ed4be3b829b --- /dev/null +++ b/tests/crashes/153375.rs @@ -0,0 +1,15 @@ +//@ known-bug: #153375 +//@ aux-build: aux153375.rs +extern crate aux153375; +use aux153375::Request; + +struct Bar<'ws>(&'ws ()); + +impl<'ws> Request for Bar<'ws> { + type A<'a> + = u8 + where + Self: 'a; + + fn f(_: Self::A<'_>) -> impl Sized {} +} diff --git a/tests/crashes/153947.rs b/tests/crashes/153947.rs new file mode 100644 index 0000000000000..39bc8c074cfc0 --- /dev/null +++ b/tests/crashes/153947.rs @@ -0,0 +1,10 @@ +//@ known-bug: #153947 +#![expect(drop_bounds)] +pub struct Thing(T) where [T]: Sized, Self: Drop; +impl Drop for Thing where [T]: Sized, Self: Drop { + fn drop(&mut self) {} +} +impl Drop for Thing where [T]: Sized, Self: Drop { + fn drop(&mut self) {} +} +fn main() {} diff --git a/tests/crashes/154296.rs b/tests/crashes/154296.rs new file mode 100644 index 0000000000000..d904a4d82e426 --- /dev/null +++ b/tests/crashes/154296.rs @@ -0,0 +1,12 @@ +//@ known-bug: #154296 +//@ edition: 2024 +mod m1 { + mod inner { + pub struct S; + } + pub use inner::*; + #[derive(Debug)] + pub struct S; +} +use m1::*; +use S; diff --git a/tests/crashes/154779.rs b/tests/crashes/154779.rs new file mode 100644 index 0000000000000..e6c03e88b0b90 --- /dev/null +++ b/tests/crashes/154779.rs @@ -0,0 +1,4 @@ +//@ known-bug: #154779 +struct Data([[&'static str]; 1]); +const _: &'static Data = &*(&[] as *const Data) ; +fn main() {} diff --git a/tests/crashes/154782.rs b/tests/crashes/154782.rs new file mode 100644 index 0000000000000..d5fdfb84b48ce --- /dev/null +++ b/tests/crashes/154782.rs @@ -0,0 +1,9 @@ +//@ known-bug: #154782 +//@ edition: 2024 +#![feature(pin_ergonomics)] +use core::pin::Pin; +fn test_idempotency(x: Pin<&mut T>) { + || { + x.poll(loop {}); + }; +} diff --git a/tests/crashes/154871.rs b/tests/crashes/154871.rs new file mode 100644 index 0000000000000..c106028623d75 --- /dev/null +++ b/tests/crashes/154871.rs @@ -0,0 +1,7 @@ +//@ known-bug: #154871 +struct Struct { + b: unsafe<> (), +} +fn main() { + std::ptr::null::; +} diff --git a/tests/crashes/auxiliary/aux132985.rs b/tests/crashes/auxiliary/aux132985.rs deleted file mode 100644 index 7ae5567bdc59d..0000000000000 --- a/tests/crashes/auxiliary/aux132985.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![feature(adt_const_params)] - -use std::marker::ConstParamTy; - -#[derive(Eq, PartialEq, ConstParamTy)] -pub struct Foo; diff --git a/tests/crashes/auxiliary/aux153375.rs b/tests/crashes/auxiliary/aux153375.rs new file mode 100644 index 0000000000000..c5f09489181b6 --- /dev/null +++ b/tests/crashes/auxiliary/aux153375.rs @@ -0,0 +1,6 @@ +pub trait Request { + type A<'a> + where + Self: 'a; + fn f(_: Self::A<'_>) -> impl Sized; +} diff --git a/tests/crashes/auxiliary/overlapping_spans_helper.rs b/tests/crashes/auxiliary/overlapping_spans_helper.rs deleted file mode 100644 index e449fcd36c376..0000000000000 --- a/tests/crashes/auxiliary/overlapping_spans_helper.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Auxiliary lib for the issue 147973 regression test with ICEs due to overlapping spans. - -#[macro_export] -macro_rules! identity { - ($x:ident) => { - $x - }; -} - -#[macro_export] -macro_rules! do_loop { - ($x:ident) => { - for $crate::identity!($x) in $x {} - }; -} From d250a75f98064507f81dbe31752647170cc7316f Mon Sep 17 00:00:00 2001 From: zakrad <49591476+zakrad@users.noreply.github.com> Date: Tue, 18 Aug 2026 16:13:25 +0330 Subject: [PATCH 2/9] Add regression test for extern crate alias shadowed by a module of the same name --- tests/ui/rust-2018/auxiliary/time.rs | 3 +++ ...rn-crate-alias-shadowed-by-module-55759.rs | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/ui/rust-2018/auxiliary/time.rs create mode 100644 tests/ui/rust-2018/extern-crate-alias-shadowed-by-module-55759.rs diff --git a/tests/ui/rust-2018/auxiliary/time.rs b/tests/ui/rust-2018/auxiliary/time.rs new file mode 100644 index 0000000000000..a03d3d1fb5341 --- /dev/null +++ b/tests/ui/rust-2018/auxiliary/time.rs @@ -0,0 +1,3 @@ +pub fn now() -> u32 { + 0 +} diff --git a/tests/ui/rust-2018/extern-crate-alias-shadowed-by-module-55759.rs b/tests/ui/rust-2018/extern-crate-alias-shadowed-by-module-55759.rs new file mode 100644 index 0000000000000..c2ca2a970a169 --- /dev/null +++ b/tests/ui/rust-2018/extern-crate-alias-shadowed-by-module-55759.rs @@ -0,0 +1,19 @@ +// Regression test for . +// `rust_2018_idioms` used to suggest rewriting this `extern crate` as `use time as +// std_time;`. Because a local module shares the crate's name, the rewritten import is +// ambiguous (E0659), so applying the suggestion produced code that did not compile. +//@ edition: 2018 +//@ aux-build: time.rs +//@ check-pass + +#![warn(rust_2018_idioms)] + +extern crate time as std_time; + +pub mod time { + pub fn f() { + let _ = crate::std_time::now(); + } +} + +fn main() {} From c0627a6e1e4363feaac3afbf48bea14daa523cbd Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 16 Aug 2026 18:11:39 -0400 Subject: [PATCH 3/9] Move dist-x86_64-linux-alt to EC2 too This also cleans up some legacy runner configurations that aren't currently used. We can always restore these if needed and it's distracting when trying to identify what GitHub large runners are getting used. dist-alt.sh does less (no opt-dist) so we run it on a less powerful EC2 instance. --- src/ci/github-actions/jobs.yml | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 08121a9a693b7..ce84df0d6d9bb 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -15,18 +15,6 @@ runners: free_disk: true <<: *base-job - - &job-linux-4c-largedisk - os: ubuntu-24.04-4core-16gb - <<: *base-job - - - &job-linux-8c - os: ubuntu-24.04-8core-32gb - <<: *base-job - - - &job-linux-16c - os: ubuntu-24.04-16core-64gb - <<: *base-job - - &job-macos-15 os: macos-15 # macOS 15 Arm64 <<: *base-job @@ -65,14 +53,12 @@ runners: os: codebuild-ubuntu-22-36c-$github.run_id-$github.run_attempt <<: *base-job - - &job-linux-8c-codebuild - free_disk: true - codebuild: true - os: codebuild-ubuntu-22-8c-$github.run_id-$github.run_attempt + - &job-linux-x86-32c-ec2 + os: ec2-x86_64ami-c8a.8xlarge-x64-linux-$github.run_id-$github.run_attempt <<: *base-job - - &job-linux-32c-ec2 - os: ec2-x86_64ami-c8a.8xlarge-x64-linux-$github.run_id-$github.run_attempt + - &job-linux-x86-8c-ec2 + os: ec2-x86_64ami-m8a.2xlarge-x64-linux-$github.run_id-$github.run_attempt <<: *base-job envs: @@ -179,7 +165,7 @@ pr: # These jobs automatically inherit envs.try, to avoid repeating # it in each job definition. try: - - <<: [*job-dist-x86_64-linux, *job-linux-32c-ec2] + - <<: [*job-dist-x86_64-linux, *job-linux-x86-32c-ec2] name: dist-x86_64-linux-quick # Jobs that only run when explicitly invoked in one of the following ways: @@ -203,7 +189,7 @@ optional: DIST_TRY_BUILD: 1 # We repeat the try job here so that it can be explicitly executed using `@bors try jobs`, to test # full x64 Linux dist try builds on EC2. - - <<: [*job-dist-x86_64-linux, *job-linux-32c-ec2] + - <<: [*job-dist-x86_64-linux, *job-linux-x86-32c-ec2] name: dist-x86_64-linux-quick # Main CI jobs that have to be green to merge a commit into the default branch. @@ -312,14 +298,14 @@ auto: - name: dist-x86_64-illumos <<: *job-linux-4c - - <<: [*job-dist-x86_64-linux, *job-linux-32c-ec2] + - <<: [*job-dist-x86_64-linux, *job-linux-x86-32c-ec2] - name: dist-x86_64-linux-alt env: IMAGE: dist-x86_64-linux CODEGEN_BACKENDS: llvm,cranelift DOCKER_SCRIPT: dist-alt.sh - <<: *job-linux-8c + <<: *job-linux-x86-8c-ec2 - name: dist-x86_64-musl env: From fe8985b831870f79c42db01ed86c8507baed36b6 Mon Sep 17 00:00:00 2001 From: KevinA-cpu Date: Sat, 22 Aug 2026 17:13:55 +0700 Subject: [PATCH 4/9] Add regression test for private fields lint --- ...tuple-struct-field-self-arg-issue-81447.rs | 20 +++++++++++++ ...e-struct-field-self-arg-issue-81447.stderr | 29 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tests/ui/privacy/private-tuple-struct-field-self-arg-issue-81447.rs create mode 100644 tests/ui/privacy/private-tuple-struct-field-self-arg-issue-81447.stderr diff --git a/tests/ui/privacy/private-tuple-struct-field-self-arg-issue-81447.rs b/tests/ui/privacy/private-tuple-struct-field-self-arg-issue-81447.rs new file mode 100644 index 0000000000000..84c1d7b883a7b --- /dev/null +++ b/tests/ui/privacy/private-tuple-struct-field-self-arg-issue-81447.rs @@ -0,0 +1,20 @@ +// Regression test for https://github.com/rust-lang/rust/issues/81447 +// Checks that the lint for private fields is prioritized over the self lint. + +mod some_module { + pub struct Test(T); +} + +use some_module::Test; + +struct TestBuilder; + +impl TestBuilder { + fn build(self) -> Test { + //~^ ERROR missing generics for struct `Test` + Test(self) + //~^ ERROR cannot initialize a tuple struct which contains private fields + } +} + +fn main() {} diff --git a/tests/ui/privacy/private-tuple-struct-field-self-arg-issue-81447.stderr b/tests/ui/privacy/private-tuple-struct-field-self-arg-issue-81447.stderr new file mode 100644 index 0000000000000..eb1e32a7d02ab --- /dev/null +++ b/tests/ui/privacy/private-tuple-struct-field-self-arg-issue-81447.stderr @@ -0,0 +1,29 @@ +error[E0107]: missing generics for struct `Test` + --> $DIR/private-tuple-struct-field-self-arg-issue-81447.rs:13:23 + | +LL | fn build(self) -> Test { + | ^^^^ expected 1 generic argument + | +note: struct defined here, with 1 generic parameter: `T` + --> $DIR/private-tuple-struct-field-self-arg-issue-81447.rs:5:16 + | +LL | pub struct Test(T); + | ^^^^ - +help: add missing generic argument + | +LL | fn build(self) -> Test { + | +++ + +error[E0423]: cannot initialize a tuple struct which contains private fields + --> $DIR/private-tuple-struct-field-self-arg-issue-81447.rs:15:9 + | +LL | Test(self) + | ^^^^ + | + = note: a struct named `Test` exists in another namespace + = note: constructor is not visible here due to private fields + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0107, E0423. +For more information about an error, try `rustc --explain E0107`. From d305e6e8b19ff4a1c783498009f451dc4137f4fa Mon Sep 17 00:00:00 2001 From: xyz-harshal Date: Mon, 3 Aug 2026 15:50:31 +0530 Subject: [PATCH 5/9] Add more context to dyn-compatibility diagnostic for receiver-less associated functions Explains that a method without `self` cannot be dispatched through a trait object's vtable, and that `where Self: Sized` explicitly opts a method out of dyn-compatibility requirements. --- compiler/rustc_middle/src/traits/mod.rs | 7 +-- .../avoid-ice-on-warning-3.old.stderr | 8 +-- .../dyn-incompat-const-slice.stderr | 8 +-- tests/ui/dyn-compatibility/no-static.stderr | 8 +-- .../spurious-dyn-compat-errors-58734.stderr | 4 +- ...onstructor-prevents-dyn-no-api-guidance.rs | 9 ++++ ...ructor-prevents-dyn-no-api-guidance.stderr | 54 +++++++++++++++++++ .../dyn-compatibility/taint-const-eval.stderr | 4 +- ...-trait-in-return-position-dyn-trait.stderr | 16 +++--- ...recursive-trait-fn-sig-issue-142064.stderr | 8 +-- .../unsizing-wfcheck-issue-127299.stderr | 8 +-- ...mpatible-trait-should-use-self-2021.stderr | 4 +- ...-incompatible-trait-should-use-self.stderr | 4 +- ...atible-trait-should-use-where-sized.stderr | 4 +- tests/ui/traits/issue-72410.stderr | 4 +- .../missing-for-type-in-impl.e2015.stderr | 4 +- ...alize-fresh-infer-vars-issue-103626.stderr | 4 +- tests/ui/traits/object/safety.stderr | 4 +- 18 files changed, 113 insertions(+), 49 deletions(-) create mode 100644 tests/ui/dyn-compatibility/static-constructor-prevents-dyn-no-api-guidance.rs create mode 100644 tests/ui/dyn-compatibility/static-constructor-prevents-dyn-no-api-guidance.stderr diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 4126531229b48..c6c9c24547dd5 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -925,7 +925,8 @@ impl DynCompatibilityViolationSolution { err.span_suggestion( add_self_sugg.1, format!( - "consider turning `{name}` into a method by giving it a `&self` argument" + "consider turning `{name}` into a method by giving it a `&self` \ + argument, so that it is accessible through the trait object's vtable" ), add_self_sugg.0, Applicability::MaybeIncorrect, @@ -933,8 +934,8 @@ impl DynCompatibilityViolationSolution { err.span_suggestion( make_sized_sugg.1, format!( - "alternatively, consider constraining `{name}` so it does not apply to \ - trait objects" + "alternatively, consider constraining `{name}` so it is explicitly \ + marked as not applying to trait objects" ), make_sized_sugg.0, Applicability::MaybeIncorrect, diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr index 7b299fa3cab74..72d9f3b5b736d 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr @@ -79,11 +79,11 @@ LL | trait A { fn g(b: B) -> B; } | - ^ ...because associated function `g` has no `self` parameter | | | this trait is not dyn compatible... -help: consider turning `g` into a method by giving it a `&self` argument +help: consider turning `g` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | trait A { fn g(&self, b: B) -> B; } | ++++++ -help: alternatively, consider constraining `g` so it does not apply to trait objects +help: alternatively, consider constraining `g` so it is explicitly marked as not applying to trait objects | LL | trait A { fn g(b: B) -> B where Self: Sized; } | +++++++++++++++++ @@ -121,11 +121,11 @@ LL | trait B { fn f(a: A) -> A; } | - ^ ...because associated function `f` has no `self` parameter | | | this trait is not dyn compatible... -help: consider turning `f` into a method by giving it a `&self` argument +help: consider turning `f` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | trait B { fn f(&self, a: A) -> A; } | ++++++ -help: alternatively, consider constraining `f` so it does not apply to trait objects +help: alternatively, consider constraining `f` so it is explicitly marked as not applying to trait objects | LL | trait B { fn f(a: A) -> A where Self: Sized; } | +++++++++++++++++ diff --git a/tests/ui/dyn-compatibility/dyn-incompat-const-slice.stderr b/tests/ui/dyn-compatibility/dyn-incompat-const-slice.stderr index 46ed6a48a77fa..2978278bb286d 100644 --- a/tests/ui/dyn-compatibility/dyn-incompat-const-slice.stderr +++ b/tests/ui/dyn-compatibility/dyn-incompat-const-slice.stderr @@ -13,11 +13,11 @@ LL | trait Qiz { LL | fn qiz(); | ^^^ ...because associated function `qiz` has no `self` parameter = help: only type `Foo` implements `Qiz`; consider using it directly instead. -help: consider turning `qiz` into a method by giving it a `&self` argument +help: consider turning `qiz` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn qiz(&self); | +++++ -help: alternatively, consider constraining `qiz` so it does not apply to trait objects +help: alternatively, consider constraining `qiz` so it is explicitly marked as not applying to trait objects | LL | fn qiz() where Self: Sized; | +++++++++++++++++ @@ -37,11 +37,11 @@ LL | trait Qiz { LL | fn qiz(); | ^^^ ...because associated function `qiz` has no `self` parameter = help: only type `Foo` implements `Qiz`; consider using it directly instead. -help: consider turning `qiz` into a method by giving it a `&self` argument +help: consider turning `qiz` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn qiz(&self); | +++++ -help: alternatively, consider constraining `qiz` so it does not apply to trait objects +help: alternatively, consider constraining `qiz` so it is explicitly marked as not applying to trait objects | LL | fn qiz() where Self: Sized; | +++++++++++++++++ diff --git a/tests/ui/dyn-compatibility/no-static.stderr b/tests/ui/dyn-compatibility/no-static.stderr index c1d5dd6f562f7..836b98471ec80 100644 --- a/tests/ui/dyn-compatibility/no-static.stderr +++ b/tests/ui/dyn-compatibility/no-static.stderr @@ -13,11 +13,11 @@ LL | trait Foo { LL | fn foo() {} | ^^^ ...because associated function `foo` has no `self` parameter = help: only type `Bar` implements `Foo`; consider using it directly instead. -help: consider turning `foo` into a method by giving it a `&self` argument +help: consider turning `foo` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn foo(&self) {} | +++++ -help: alternatively, consider constraining `foo` so it does not apply to trait objects +help: alternatively, consider constraining `foo` so it is explicitly marked as not applying to trait objects | LL | fn foo() where Self: Sized {} | +++++++++++++++++ @@ -37,11 +37,11 @@ LL | trait Foo { LL | fn foo() {} | ^^^ ...because associated function `foo` has no `self` parameter = help: only type `Bar` implements `Foo`; consider using it directly instead. -help: consider turning `foo` into a method by giving it a `&self` argument +help: consider turning `foo` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn foo(&self) {} | +++++ -help: alternatively, consider constraining `foo` so it does not apply to trait objects +help: alternatively, consider constraining `foo` so it is explicitly marked as not applying to trait objects | LL | fn foo() where Self: Sized {} | +++++++++++++++++ diff --git a/tests/ui/dyn-compatibility/spurious-dyn-compat-errors-58734.stderr b/tests/ui/dyn-compatibility/spurious-dyn-compat-errors-58734.stderr index 5461579b911f5..d10e95f16a950 100644 --- a/tests/ui/dyn-compatibility/spurious-dyn-compat-errors-58734.stderr +++ b/tests/ui/dyn-compatibility/spurious-dyn-compat-errors-58734.stderr @@ -28,11 +28,11 @@ LL | trait Trait { LL | fn dyn_incompatible() -> Self; | ^^^^^^^^^^^^^^^^ ...because associated function `dyn_incompatible` has no `self` parameter = help: only type `()` implements `Trait`; consider using it directly instead. -help: consider turning `dyn_incompatible` into a method by giving it a `&self` argument +help: consider turning `dyn_incompatible` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn dyn_incompatible(&self) -> Self; | +++++ -help: alternatively, consider constraining `dyn_incompatible` so it does not apply to trait objects +help: alternatively, consider constraining `dyn_incompatible` so it is explicitly marked as not applying to trait objects | LL | fn dyn_incompatible() -> Self where Self: Sized; | +++++++++++++++++ diff --git a/tests/ui/dyn-compatibility/static-constructor-prevents-dyn-no-api-guidance.rs b/tests/ui/dyn-compatibility/static-constructor-prevents-dyn-no-api-guidance.rs new file mode 100644 index 0000000000000..71e096c5d31f1 --- /dev/null +++ b/tests/ui/dyn-compatibility/static-constructor-prevents-dyn-no-api-guidance.rs @@ -0,0 +1,9 @@ +trait Factory { + fn create() -> Box; + //~^ ERROR the trait `Factory` is not dyn compatible +} + +fn use_factory(_: &dyn Factory) {} +//~^ ERROR the trait `Factory` is not dyn compatible + +fn main() {} diff --git a/tests/ui/dyn-compatibility/static-constructor-prevents-dyn-no-api-guidance.stderr b/tests/ui/dyn-compatibility/static-constructor-prevents-dyn-no-api-guidance.stderr new file mode 100644 index 0000000000000..3a670f2741473 --- /dev/null +++ b/tests/ui/dyn-compatibility/static-constructor-prevents-dyn-no-api-guidance.stderr @@ -0,0 +1,54 @@ +error[E0038]: the trait `Factory` is not dyn compatible + --> $DIR/static-constructor-prevents-dyn-no-api-guidance.rs:6:20 + | +LL | fn use_factory(_: &dyn Factory) {} + | ^^^^^^^^^^^ `Factory` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/static-constructor-prevents-dyn-no-api-guidance.rs:2:8 + | +LL | trait Factory { + | ------- this trait is not dyn compatible... +LL | fn create() -> Box; + | ^^^^^^ ...because associated function `create` has no `self` parameter +help: consider turning `create` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable + | +LL | fn create(&self) -> Box; + | +++++ +help: alternatively, consider constraining `create` so it is explicitly marked as not applying to trait objects + | +LL | fn create() -> Box where Self: Sized; + | +++++++++++++++++ + +error[E0038]: the trait `Factory` is not dyn compatible + --> $DIR/static-constructor-prevents-dyn-no-api-guidance.rs:2:24 + | +LL | fn create() -> Box; + | ^^^^^^^^^^^ `Factory` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/static-constructor-prevents-dyn-no-api-guidance.rs:2:8 + | +LL | trait Factory { + | ------- this trait is not dyn compatible... +LL | fn create() -> Box; + | ^^^^^^ ...because associated function `create` has no `self` parameter +help: consider turning `create` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable + | +LL | fn create(&self) -> Box; + | +++++ +help: alternatively, consider constraining `create` so it is explicitly marked as not applying to trait objects + | +LL | fn create() -> Box where Self: Sized; + | +++++++++++++++++ +help: you might have meant to use `Self` to refer to the implementing type + | +LL - fn create() -> Box; +LL + fn create() -> Box; + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/dyn-compatibility/taint-const-eval.stderr b/tests/ui/dyn-compatibility/taint-const-eval.stderr index e4be9870fdc44..acbeb27fdc963 100644 --- a/tests/ui/dyn-compatibility/taint-const-eval.stderr +++ b/tests/ui/dyn-compatibility/taint-const-eval.stderr @@ -12,11 +12,11 @@ LL | trait Qux { | --- this trait is not dyn compatible... LL | fn bar(); | ^^^ ...because associated function `bar` has no `self` parameter -help: consider turning `bar` into a method by giving it a `&self` argument +help: consider turning `bar` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn bar(&self); | +++++ -help: alternatively, consider constraining `bar` so it does not apply to trait objects +help: alternatively, consider constraining `bar` so it is explicitly marked as not applying to trait objects | LL | fn bar() where Self: Sized; | +++++++++++++++++ diff --git a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr index a8787a01a6f6a..fa38465318b2b 100644 --- a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr +++ b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr @@ -17,11 +17,11 @@ LL | fn foo() -> Self; B consider defining an enum where each variant holds one of these types, implementing `DynIncompatible` for this new enum and using it instead -help: consider turning `foo` into a method by giving it a `&self` argument +help: consider turning `foo` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn foo(&self) -> Self; | +++++ -help: alternatively, consider constraining `foo` so it does not apply to trait objects +help: alternatively, consider constraining `foo` so it is explicitly marked as not applying to trait objects | LL | fn foo() -> Self where Self: Sized; | +++++++++++++++++ @@ -67,11 +67,11 @@ LL | fn foo() -> Self; B consider defining an enum where each variant holds one of these types, implementing `DynIncompatible` for this new enum and using it instead -help: consider turning `foo` into a method by giving it a `&self` argument +help: consider turning `foo` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn foo(&self) -> Self; | +++++ -help: alternatively, consider constraining `foo` so it does not apply to trait objects +help: alternatively, consider constraining `foo` so it is explicitly marked as not applying to trait objects | LL | fn foo() -> Self where Self: Sized; | +++++++++++++++++ @@ -100,11 +100,11 @@ help: consider using an opaque type instead LL - fn car() -> dyn DynIncompatible { LL + fn car() -> impl DynIncompatible { | -help: consider turning `foo` into a method by giving it a `&self` argument +help: consider turning `foo` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn foo(&self) -> Self; | +++++ -help: alternatively, consider constraining `foo` so it does not apply to trait objects +help: alternatively, consider constraining `foo` so it is explicitly marked as not applying to trait objects | LL | fn foo() -> Self where Self: Sized; | +++++++++++++++++ @@ -134,11 +134,11 @@ help: consider using an opaque type instead LL - fn car() -> dyn DynIncompatible { LL + fn car() -> impl DynIncompatible { | -help: consider turning `foo` into a method by giving it a `&self` argument +help: consider turning `foo` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn foo(&self) -> Self; | +++++ -help: alternatively, consider constraining `foo` so it does not apply to trait objects +help: alternatively, consider constraining `foo` so it is explicitly marked as not applying to trait objects | LL | fn foo() -> Self where Self: Sized; | +++++++++++++++++ diff --git a/tests/ui/parallel-rustc/recursive-trait-fn-sig-issue-142064.stderr b/tests/ui/parallel-rustc/recursive-trait-fn-sig-issue-142064.stderr index c26df459d2564..6071fddbdac1c 100644 --- a/tests/ui/parallel-rustc/recursive-trait-fn-sig-issue-142064.stderr +++ b/tests/ui/parallel-rustc/recursive-trait-fn-sig-issue-142064.stderr @@ -40,11 +40,11 @@ LL | trait A { fn foo() -> A; } | - ^^^ ...because associated function `foo` has no `self` parameter | | | this trait is not dyn compatible... -help: consider turning `foo` into a method by giving it a `&self` argument +help: consider turning `foo` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | trait A { fn foo(&self) -> A; } | +++++ -help: alternatively, consider constraining `foo` so it does not apply to trait objects +help: alternatively, consider constraining `foo` so it is explicitly marked as not applying to trait objects | LL | trait A { fn foo() -> A where Self: Sized; } | +++++++++++++++++ @@ -95,11 +95,11 @@ LL | trait A { fn foo() -> A; } | - ^^^ ...because associated function `foo` has no `self` parameter | | | this trait is not dyn compatible... -help: consider turning `foo` into a method by giving it a `&self` argument +help: consider turning `foo` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | trait A { fn foo(&self) -> A; } | +++++ -help: alternatively, consider constraining `foo` so it does not apply to trait objects +help: alternatively, consider constraining `foo` so it is explicitly marked as not applying to trait objects | LL | trait A { fn foo() -> A where Self: Sized; } | +++++++++++++++++ diff --git a/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr b/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr index e401277a0209f..e231d57a26d91 100644 --- a/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr +++ b/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr @@ -12,11 +12,11 @@ LL | trait Qux { | --- this trait is not dyn compatible... LL | fn bar() -> i32; | ^^^ ...because associated function `bar` has no `self` parameter -help: consider turning `bar` into a method by giving it a `&self` argument +help: consider turning `bar` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn bar(&self) -> i32; | +++++ -help: alternatively, consider constraining `bar` so it does not apply to trait objects +help: alternatively, consider constraining `bar` so it is explicitly marked as not applying to trait objects | LL | fn bar() -> i32 where Self: Sized; | +++++++++++++++++ @@ -51,11 +51,11 @@ LL | trait Qux { | --- this trait is not dyn compatible... LL | fn bar() -> i32; | ^^^ ...because associated function `bar` has no `self` parameter -help: consider turning `bar` into a method by giving it a `&self` argument +help: consider turning `bar` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn bar(&self) -> i32; | +++++ -help: alternatively, consider constraining `bar` so it does not apply to trait objects +help: alternatively, consider constraining `bar` so it is explicitly marked as not applying to trait objects | LL | fn bar() -> i32 where Self: Sized; | +++++++++++++++++ diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr index 4ccf65b68bf75..c3e044dbb1c14 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr @@ -32,11 +32,11 @@ LL | trait B { | - this trait is not dyn compatible... LL | fn f(a: dyn B) -> dyn B; | ^ ...because associated function `f` has no `self` parameter -help: consider turning `f` into a method by giving it a `&self` argument +help: consider turning `f` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn f(&self, a: dyn B) -> dyn B; | ++++++ -help: alternatively, consider constraining `f` so it does not apply to trait objects +help: alternatively, consider constraining `f` so it is explicitly marked as not applying to trait objects | LL | fn f(a: dyn B) -> dyn B where Self: Sized; | +++++++++++++++++ diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr index bda1d01e23ff9..11c70c89d8b41 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr @@ -32,11 +32,11 @@ LL | trait B { | - this trait is not dyn compatible... LL | fn f(a: dyn B) -> dyn B; | ^ ...because associated function `f` has no `self` parameter -help: consider turning `f` into a method by giving it a `&self` argument +help: consider turning `f` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn f(&self, a: dyn B) -> dyn B; | ++++++ -help: alternatively, consider constraining `f` so it does not apply to trait objects +help: alternatively, consider constraining `f` so it is explicitly marked as not applying to trait objects | LL | fn f(a: dyn B) -> dyn B where Self: Sized; | +++++++++++++++++ diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr index c275cdccaa8c1..17c819660f51d 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr @@ -14,11 +14,11 @@ LL | fn foo() where Self: Other, { } | ^^^ ...because associated function `foo` has no `self` parameter LL | fn bar(self: ()) {} | ^^ ...because method `bar`'s `self` parameter cannot be dispatched on -help: consider turning `foo` into a method by giving it a `&self` argument +help: consider turning `foo` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn foo(&self) where Self: Other, { } | +++++ -help: alternatively, consider constraining `foo` so it does not apply to trait objects +help: alternatively, consider constraining `foo` so it is explicitly marked as not applying to trait objects | LL | fn foo() where Self: Other, Self: Sized { } | +++++++++++ diff --git a/tests/ui/traits/issue-72410.stderr b/tests/ui/traits/issue-72410.stderr index c9e133437dd86..33feb9e4269f8 100644 --- a/tests/ui/traits/issue-72410.stderr +++ b/tests/ui/traits/issue-72410.stderr @@ -12,11 +12,11 @@ LL | pub trait Bar { | --- this trait is not dyn compatible... LL | fn map() | ^^^ ...because associated function `map` has no `self` parameter -help: consider turning `map` into a method by giving it a `&self` argument +help: consider turning `map` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn map(&self) | +++++ -help: alternatively, consider constraining `map` so it does not apply to trait objects +help: alternatively, consider constraining `map` so it is explicitly marked as not applying to trait objects | LL | where for<'a> &'a mut [dyn Bar]:, Self: Sized ; | +++++++++++++ diff --git a/tests/ui/traits/missing-for-type-in-impl.e2015.stderr b/tests/ui/traits/missing-for-type-in-impl.e2015.stderr index 1dc351eb9e730..df5d35000afce 100644 --- a/tests/ui/traits/missing-for-type-in-impl.e2015.stderr +++ b/tests/ui/traits/missing-for-type-in-impl.e2015.stderr @@ -48,11 +48,11 @@ LL | trait Foo { | --- this trait is not dyn compatible... LL | fn id(me: T) -> T; | ^^ ...because associated function `id` has no `self` parameter -help: consider turning `id` into a method by giving it a `&self` argument +help: consider turning `id` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn id(&self, me: T) -> T; | ++++++ -help: alternatively, consider constraining `id` so it does not apply to trait objects +help: alternatively, consider constraining `id` so it is explicitly marked as not applying to trait objects | LL | fn id(me: T) -> T where Self: Sized; | +++++++++++++++++ diff --git a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr index 707aa9e9713db..78a0743d9f87c 100644 --- a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr +++ b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr @@ -12,11 +12,11 @@ LL | trait FromResidual::Residual> { | ------------ this trait is not dyn compatible... LL | fn from_residual(residual: R) -> Self; | ^^^^^^^^^^^^^ ...because associated function `from_residual` has no `self` parameter -help: consider turning `from_residual` into a method by giving it a `&self` argument +help: consider turning `from_residual` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn from_residual(&self, residual: R) -> Self; | ++++++ -help: alternatively, consider constraining `from_residual` so it does not apply to trait objects +help: alternatively, consider constraining `from_residual` so it is explicitly marked as not applying to trait objects | LL | fn from_residual(residual: R) -> Self where Self: Sized; | +++++++++++++++++ diff --git a/tests/ui/traits/object/safety.stderr b/tests/ui/traits/object/safety.stderr index c5637b435262c..4536957566747 100644 --- a/tests/ui/traits/object/safety.stderr +++ b/tests/ui/traits/object/safety.stderr @@ -13,11 +13,11 @@ LL | trait Tr { LL | fn foo(); | ^^^ ...because associated function `foo` has no `self` parameter = help: only type `St` implements `Tr`; consider using it directly instead. -help: consider turning `foo` into a method by giving it a `&self` argument +help: consider turning `foo` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn foo(&self); | +++++ -help: alternatively, consider constraining `foo` so it does not apply to trait objects +help: alternatively, consider constraining `foo` so it is explicitly marked as not applying to trait objects | LL | fn foo() where Self: Sized; | +++++++++++++++++ From c88234901994f8e6f63c23a3220969f7acee4cbf Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sat, 22 Aug 2026 17:22:08 -0400 Subject: [PATCH 6/9] Add aarch64 EC2 instances to eligibility list --- rust-bors.toml | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/rust-bors.toml b/rust-bors.toml index 94a1f7cf60816..01a41d6ae5990 100644 --- a/rust-bors.toml +++ b/rust-bors.toml @@ -84,20 +84,34 @@ label_prefix = "ec2" region = "us-east-2" images = { "x86_64ami" = "latest-gha-runner-ami" + "arm64ami" = "latest-gha-runner-ami-arm64" } jit_runner = "organization" -# Prices per hour of on-demand compute in us-east-2 (as of Aug 2026) -# See rough assessment of build speed for dist-x86_64-quick in https://github.com/rust-lang/simpleinfra/issues/1132 -# m8a.2x 8 vCPU, 32 GB $0.48688/hr -# c8a.4x 16 vCPU, 32 GB $0.86216/hr -# c8a.8x 32 vCPU, 64 GB $1.72432/hr -# c8a.12x 48 vCPU, 96 GB $2.58648/hr -# CodeBuild 36 vCPU $4.78799/hr allowed_instances = [ + # AMD Zen 5 (x86_64) instances, a subset of these is used in production. + # Prices per hour of on-demand compute in us-east-2 (as of Aug 2026) + # See rough assessment of build speed for dist-x86_64-quick in https://github.com/rust-lang/simpleinfra/issues/1132 + # m8a.2x 8 vCPU, 32 GB $0.48688/hr + # c8a.4x 16 vCPU, 32 GB $0.86216/hr + # c8a.8x 32 vCPU, 64 GB $1.72432/hr + # c8a.12x 48 vCPU, 96 GB $2.58648/hr + # CodeBuild 36 vCPU $4.78799/hr "m8a.2xlarge", "c8a.4xlarge", "c8a.8xlarge", "c8a.12xlarge", + + # Graviton 4 (aarch64) instances, currently just for experimentation + "m8g.2xlarge", + "c8g.4xlarge", + "c8g.8xlarge", + "c8g.12xlarge", + + # Graviton 5 (aarch64) instances, currently just for experimentation + "m9g.2xlarge", + "c9g.4xlarge", + "c9g.8xlarge", + "c9g.12xlarge", ] # Enable unrolling of rollup member PRs after rollup merge From 8274a0e238379d0d809f511c7cdcd5b8152ead34 Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Fri, 17 Jul 2026 16:41:58 +0600 Subject: [PATCH 7/9] suggest removing a trailing semicolon when a closure argument fails a trait bound extends the remove-this-semicolon suggestion for `-> impl Trait` bodies (https://github.com/rust-lang/rust/pull/81407, https://github.com/rust-lang/rust/pull/95758) to closure arguments. only fires when the type behind the semicolon satisfies the failed bound, never when several closure arguments qualify. before: error[E0277]: the trait bound (): Bar is not satisfied 6 | bar(|| { 5u8; }) | ^^^^^^^^^^^^^^^^ the trait Bar is not implemented for () after: error[E0277]: the trait bound (): Bar is not satisfied 6 | bar(|| { 5u8; }) | ^^^^^^^^^----^^^ | | | | | | | help: remove this semicolon | | this expression has type u8, which implements Bar | the trait Bar is not implemented for () fixes https://github.com/rust-lang/rust/issues/54771 --- .../src/error_reporting/traits/suggestions.rs | 108 ++++++++++++++++++ .../closure-arg-trailing-semicolon.rs | 45 ++++++++ .../closure-arg-trailing-semicolon.stderr | 102 +++++++++++++++++ 3 files changed, 255 insertions(+) create mode 100644 tests/ui/suggestions/closure-arg-trailing-semicolon.rs create mode 100644 tests/ui/suggestions/closure-arg-trailing-semicolon.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 01b3026ff4e5b..89623f4cdd0af 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2567,6 +2567,114 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); return true; } + self.suggest_semicolon_removal_in_closure_arg(obligation, err, trait_pred) + } + + /// Detect when a closure argument returns `()` because of a trailing semicolon and that makes + /// a trait bound on the function's generic param fail, and suggest removing the semicolon: + /// + /// ```text + /// fn bar(_: impl Fn() -> R) {} + /// bar(|| { 5u8; }) + /// // - help: remove this semicolon + /// ``` + fn suggest_semicolon_removal_in_closure_arg( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diag<'_>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool { + if !trait_pred.self_ty().skip_binder().is_unit() { + return false; + } + let &ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, _) = + obligation.cause.code().peel_derives() + else { + return false; + }; + let hir::Node::Expr(expr) = self.tcx.hir_node(hir_id) else { + return false; + }; + let Some(typeck_results) = &self.typeck_results else { + return false; + }; + let args: Vec<&hir::Expr<'_>> = + match expr.kind { + hir::ExprKind::MethodCall(_, receiver, args, _) => { + iter::once(receiver).chain(args).collect() + } + // For a call like `bar(..)` the obligation is attached to the callee path expression, + // so the arguments live in its parent. + _ => match self.tcx.parent_hir_node(expr.hir_id) { + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Call(callee, args), .. + }) if callee.hir_id == expr.hir_id => args.iter().collect(), + _ => return false, + }, + }; + let mut candidates = args.into_iter().filter_map(|arg| { + // The error can be reported while the closure argument is still being checked, before + // its own type is recorded, so identify closures syntactically and only fall back to + // the argument's type (e.g. for a closure bound to a variable and passed by path). + let closure_def_id = match arg.kind { + hir::ExprKind::Closure(closure) => closure.def_id, + _ => match typeck_results.expr_ty_adjusted_opt(arg).map(|ty| { + *self.resolve_vars_if_possible(ty).peel_refs().kind() + }) { + Some(ty::Closure(def_id, _)) => def_id.as_local()?, + _ => return None, + }, + }; + let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) = + self.tcx.hir_node_by_def_id(closure_def_id) + else { + return None; + }; + let body_value = self.tcx.hir_body(closure.body).value; + if let hir::ExprKind::Block(block @ hir::Block { expr: None, .. }, None) = + body_value.kind + && let [.., stmt] = block.stmts + && !stmt.span.from_expansion() + && let hir::StmtKind::Semi(tail_expr) = stmt.kind + && !matches!(tail_expr.kind, hir::ExprKind::Err(_)) + // Tie the failing `(): Trait` predicate to this closure through its return type. + // The already-checked body is in the typeck results even when the closure isn't. + && let Some(ret_ty) = typeck_results.expr_ty_opt(body_value) + && self.resolve_vars_if_possible(ret_ty).is_unit() + // Only suggest this if the expression behind the semicolon implements the predicate + && let Some(ty) = + typeck_results.expr_ty_opt(tail_expr).map(|ty| self.resolve_vars_if_possible(ty)) + && self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + trait_pred.map_bound(|trait_pred| (trait_pred, ty)), + )) + { + Some((stmt, tail_expr, ty)) + } else { + None + } + }); + // Only emit the suggestion when a single closure argument matches, to avoid pointing at + // an unrelated closure. + if let Some((stmt, tail_expr, ty)) = candidates.next() + && candidates.next().is_none() + { + err.span_label( + tail_expr.span, + format!( + "this expression has type `{}`, which implements `{}`", + ty, + trait_pred.print_modifiers_and_trait_path() + ), + ); + err.span_suggestion( + self.tcx.sess.source_map().end_point(stmt.span), + "remove this semicolon", + "", + Applicability::MachineApplicable, + ); + return true; + } false } diff --git a/tests/ui/suggestions/closure-arg-trailing-semicolon.rs b/tests/ui/suggestions/closure-arg-trailing-semicolon.rs new file mode 100644 index 0000000000000..33bb6b97aa242 --- /dev/null +++ b/tests/ui/suggestions/closure-arg-trailing-semicolon.rs @@ -0,0 +1,45 @@ +//! Check that a trailing semicolon in a closure body that makes the closure return `()` and fail +//! a trait bound on the function's generic param gets a "remove this semicolon" suggestion, like +//! `-> impl Trait` function bodies already do. +//! +//! Issue: (closure case). + +trait Bar {} +impl Bar for u8 {} +//~^ HELP the trait `Bar` is implemented for `u8` +//~| HELP the trait `Bar` is implemented for `u8` +//~| HELP the trait `Bar` is implemented for `u8` + +fn bar(_: impl Fn() -> R) {} + +struct S; +impl S { + fn run(&self, _: impl Fn() -> R) {} +} + +fn main() { + bar(|| { 5u8; }); + //~^ ERROR the trait bound `(): Bar` is not satisfied + //~| HELP remove this semicolon + + S.run(|| { 5u8; }); + //~^ ERROR the trait bound `(): Bar` is not satisfied + //~| HELP remove this semicolon + + let c = || { 5u8; }; + //~^ HELP remove this semicolon + bar(c); + //~^ ERROR the trait bound `(): Bar` is not satisfied + + // No suggestion: the last statement isn't an expression with a semicolon. + bar(|| { fn why() {} }); + //~^ ERROR the trait bound `(): Bar` is not satisfied + + // No suggestion: the tail expression's type doesn't implement `Bar`. + bar(|| { "x"; }); + //~^ ERROR the trait bound `(): Bar` is not satisfied + + // No suggestion: the closure body is empty. + bar(|| {}); + //~^ ERROR the trait bound `(): Bar` is not satisfied +} diff --git a/tests/ui/suggestions/closure-arg-trailing-semicolon.stderr b/tests/ui/suggestions/closure-arg-trailing-semicolon.stderr new file mode 100644 index 0000000000000..edcf95255de5d --- /dev/null +++ b/tests/ui/suggestions/closure-arg-trailing-semicolon.stderr @@ -0,0 +1,102 @@ +error[E0277]: the trait bound `(): Bar` is not satisfied + --> $DIR/closure-arg-trailing-semicolon.rs:21:5 + | +LL | bar(|| { 5u8; }); + | ^^^^^^^^^----^^^ + | | | | + | | | help: remove this semicolon + | | this expression has type `u8`, which implements `Bar` + | the trait `Bar` is not implemented for `()` + | +note: required by a bound in `bar` + --> $DIR/closure-arg-trailing-semicolon.rs:13:11 + | +LL | fn bar(_: impl Fn() -> R) {} + | ^^^ required by this bound in `bar` + +error[E0277]: the trait bound `(): Bar` is not satisfied + --> $DIR/closure-arg-trailing-semicolon.rs:25:7 + | +LL | S.run(|| { 5u8; }); + | ^^^ ---- help: remove this semicolon + | | | + | | this expression has type `u8`, which implements `Bar` + | the trait `Bar` is not implemented for `()` + | +note: required by a bound in `S::run` + --> $DIR/closure-arg-trailing-semicolon.rs:17:15 + | +LL | fn run(&self, _: impl Fn() -> R) {} + | ^^^ required by this bound in `S::run` + +error[E0277]: the trait bound `(): Bar` is not satisfied + --> $DIR/closure-arg-trailing-semicolon.rs:31:5 + | +LL | let c = || { 5u8; }; + | ---- help: remove this semicolon + | | + | this expression has type `u8`, which implements `Bar` +LL | +LL | bar(c); + | ^^^^^^ the trait `Bar` is not implemented for `()` + | +note: required by a bound in `bar` + --> $DIR/closure-arg-trailing-semicolon.rs:13:11 + | +LL | fn bar(_: impl Fn() -> R) {} + | ^^^ required by this bound in `bar` + +error[E0277]: the trait bound `(): Bar` is not satisfied + --> $DIR/closure-arg-trailing-semicolon.rs:35:5 + | +LL | bar(|| { fn why() {} }); + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `()` + | +help: the trait `Bar` is implemented for `u8` + --> $DIR/closure-arg-trailing-semicolon.rs:8:1 + | +LL | impl Bar for u8 {} + | ^^^^^^^^^^^^^^^ +note: required by a bound in `bar` + --> $DIR/closure-arg-trailing-semicolon.rs:13:11 + | +LL | fn bar(_: impl Fn() -> R) {} + | ^^^ required by this bound in `bar` + +error[E0277]: the trait bound `(): Bar` is not satisfied + --> $DIR/closure-arg-trailing-semicolon.rs:39:5 + | +LL | bar(|| { "x"; }); + | ^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `()` + | +help: the trait `Bar` is implemented for `u8` + --> $DIR/closure-arg-trailing-semicolon.rs:8:1 + | +LL | impl Bar for u8 {} + | ^^^^^^^^^^^^^^^ +note: required by a bound in `bar` + --> $DIR/closure-arg-trailing-semicolon.rs:13:11 + | +LL | fn bar(_: impl Fn() -> R) {} + | ^^^ required by this bound in `bar` + +error[E0277]: the trait bound `(): Bar` is not satisfied + --> $DIR/closure-arg-trailing-semicolon.rs:43:5 + | +LL | bar(|| {}); + | ^^^^^^^^^^ the trait `Bar` is not implemented for `()` + | +help: the trait `Bar` is implemented for `u8` + --> $DIR/closure-arg-trailing-semicolon.rs:8:1 + | +LL | impl Bar for u8 {} + | ^^^^^^^^^^^^^^^ +note: required by a bound in `bar` + --> $DIR/closure-arg-trailing-semicolon.rs:13:11 + | +LL | fn bar(_: impl Fn() -> R) {} + | ^^^ required by this bound in `bar` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0277`. From bcce69e40e973f942519d7afb15005cbfc61e261 Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Sat, 8 Aug 2026 10:26:30 +0600 Subject: [PATCH 8/9] only suggest removing a closure semicolon for bounds on its return type match the failed predicate against the `FnOnce::Output` projection of the parameter the closure is passed as, so unrelated params inferred as `()` no longer fire. share the gates with the function case, downgrade to `MaybeIncorrect`. --- .../src/error_reporting/traits/suggestions.rs | 172 +++++++++++------- .../closure-arg-trailing-semicolon.rs | 17 ++ .../closure-arg-trailing-semicolon.stderr | 59 ++++-- 3 files changed, 169 insertions(+), 79 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 89623f4cdd0af..9d89db381d17f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2536,38 +2536,78 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span: Span, trait_pred: ty::PolyTraitClause<'tcx>, ) -> bool { + if !trait_pred.self_ty().skip_binder().is_unit() { + return false; + } let node = self.tcx.hir_node_by_def_id(obligation.cause.body_def_id); - if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn {sig, body: body_id, .. }, .. }) = node + if let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn { sig, body: body_id, .. }, .. + }) = node && let hir::ExprKind::Block(blk, _) = &self.tcx.hir_body(*body_id).value.kind && sig.decl.output.span().overlaps(span) - && blk.expr.is_none() - && trait_pred.self_ty().skip_binder().is_unit() - && let Some(stmt) = blk.stmts.last() + && let Some(candidate) = self.removable_trailing_semicolon(blk, obligation, trait_pred) + { + // A function body has a single return type, so keeping the value can't break any + // other use of it. + self.suggest_removing_semicolon( + err, + trait_pred, + candidate, + Applicability::MachineApplicable, + ); + return true; + } + self.suggest_semicolon_removal_in_closure_arg(obligation, err, trait_pred) + } + + /// If the value of `block` is discarded by a trailing semicolon and keeping it would satisfy + /// `trait_pred`, return that statement, its expression and the type of that expression. + fn removable_trailing_semicolon( + &self, + block: &hir::Block<'tcx>, + obligation: &PredicateObligation<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> Option<(&'tcx hir::Stmt<'tcx>, &'tcx hir::Expr<'tcx>, Ty<'tcx>)> { + if block.expr.is_none() + && let Some(stmt) = block.stmts.last() && let hir::StmtKind::Semi(expr) = stmt.kind + && !stmt.span.from_expansion() + && !matches!(expr.kind, hir::ExprKind::Err(_)) // Only suggest this if the expression behind the semicolon implements the predicate && let Some(typeck_results) = &self.typeck_results - && let Some(ty) = typeck_results.expr_ty_opt(expr) + && let Some(ty) = + typeck_results.expr_ty_opt(expr).map(|ty| self.resolve_vars_if_possible(ty)) && self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty( obligation.param_env, trait_pred.map_bound(|trait_pred| (trait_pred, ty)) )) { - err.span_label( - expr.span, - format!( - "this expression has type `{}`, which implements `{}`", - ty, - trait_pred.print_modifiers_and_trait_path() - ), - ); - err.span_suggestion( - self.tcx.sess.source_map().end_point(stmt.span), - "remove this semicolon", - "", - Applicability::MachineApplicable, - ); - return true; + Some((stmt, expr, ty)) + } else { + None } - self.suggest_semicolon_removal_in_closure_arg(obligation, err, trait_pred) + } + + fn suggest_removing_semicolon( + &self, + err: &mut Diag<'_>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + (stmt, expr, ty): (&hir::Stmt<'_>, &hir::Expr<'_>, Ty<'tcx>), + applicability: Applicability, + ) { + err.span_label( + expr.span, + format!( + "this expression has type `{}`, which implements `{}`", + ty, + trait_pred.print_modifiers_and_trait_path() + ), + ); + err.span_suggestion( + self.tcx.sess.source_map().end_point(stmt.span), + "remove this semicolon", + "", + applicability, + ); } /// Detect when a closure argument returns `()` because of a trailing semicolon and that makes @@ -2584,10 +2624,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err: &mut Diag<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { - if !trait_pred.self_ty().skip_binder().is_unit() { - return false; - } - let &ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, _) = + let &ObligationCauseCode::WhereClauseInExpr(callee_def_id, _, hir_id, idx) = obligation.cause.code().peel_derives() else { return false; @@ -2612,15 +2649,42 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => return false, }, }; - let mut candidates = args.into_iter().filter_map(|arg| { + // Work with the callee's own clauses and signature, where the failing bound is still + // written in terms of the generic param it was declared on. + let clauses = self.tcx.clauses_of(callee_def_id).instantiate_identity(self.tcx); + let Some(ty::ClauseKind::Trait(failed)) = clauses + .clauses + .get(idx) + .map(|clause| clause.as_ref().skip_norm_wip().kind().skip_binder()) + else { + return false; + }; + let sig = + self.tcx.fn_sig(callee_def_id).instantiate_identity().skip_norm_wip().skip_binder(); + let mut candidates = args.into_iter().enumerate().filter_map(|(i, arg)| { + // The bound has to be on what the closure returns. A bound on an unrelated param that + // also happened to be inferred as `()` would not be satisfied by removing a semicolon. + let declared = sig.inputs().get(i)?.peel_refs(); + if !clauses.clauses.iter().any(|clause| { + matches!( + clause.as_ref().skip_norm_wip().kind().skip_binder(), + ty::ClauseKind::Projection(proj) + if self.tcx.is_lang_item(proj.def_id(), LangItem::FnOnceOutput) + && proj.projection_term.self_ty() == declared + && proj.term.as_type() == Some(failed.self_ty()) + ) + }) { + return None; + } // The error can be reported while the closure argument is still being checked, before // its own type is recorded, so identify closures syntactically and only fall back to // the argument's type (e.g. for a closure bound to a variable and passed by path). let closure_def_id = match arg.kind { hir::ExprKind::Closure(closure) => closure.def_id, - _ => match typeck_results.expr_ty_adjusted_opt(arg).map(|ty| { - *self.resolve_vars_if_possible(ty).peel_refs().kind() - }) { + _ => match typeck_results + .expr_ty_adjusted_opt(arg) + .map(|ty| *self.resolve_vars_if_possible(ty).peel_refs().kind()) + { Some(ty::Closure(def_id, _)) => def_id.as_local()?, _ => return None, }, @@ -2630,48 +2694,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { else { return None; }; - let body_value = self.tcx.hir_body(closure.body).value; - if let hir::ExprKind::Block(block @ hir::Block { expr: None, .. }, None) = - body_value.kind - && let [.., stmt] = block.stmts - && !stmt.span.from_expansion() - && let hir::StmtKind::Semi(tail_expr) = stmt.kind - && !matches!(tail_expr.kind, hir::ExprKind::Err(_)) - // Tie the failing `(): Trait` predicate to this closure through its return type. - // The already-checked body is in the typeck results even when the closure isn't. - && let Some(ret_ty) = typeck_results.expr_ty_opt(body_value) - && self.resolve_vars_if_possible(ret_ty).is_unit() - // Only suggest this if the expression behind the semicolon implements the predicate - && let Some(ty) = - typeck_results.expr_ty_opt(tail_expr).map(|ty| self.resolve_vars_if_possible(ty)) - && self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty( - obligation.param_env, - trait_pred.map_bound(|trait_pred| (trait_pred, ty)), - )) - { - Some((stmt, tail_expr, ty)) - } else { - None - } + let hir::ExprKind::Block(block, None) = self.tcx.hir_body(closure.body).value.kind + else { + return None; + }; + self.removable_trailing_semicolon(block, obligation, trait_pred) }); // Only emit the suggestion when a single closure argument matches, to avoid pointing at // an unrelated closure. - if let Some((stmt, tail_expr, ty)) = candidates.next() + if let Some(candidate) = candidates.next() && candidates.next().is_none() { - err.span_label( - tail_expr.span, - format!( - "this expression has type `{}`, which implements `{}`", - ty, - trait_pred.print_modifiers_and_trait_path() - ), - ); - err.span_suggestion( - self.tcx.sess.source_map().end_point(stmt.span), - "remove this semicolon", - "", - Applicability::MachineApplicable, + // The same closure can be passed to somewhere else that expects it to return `()`, + // where keeping the value would introduce a new error. + self.suggest_removing_semicolon( + err, + trait_pred, + candidate, + Applicability::MaybeIncorrect, ); return true; } diff --git a/tests/ui/suggestions/closure-arg-trailing-semicolon.rs b/tests/ui/suggestions/closure-arg-trailing-semicolon.rs index 33bb6b97aa242..3c37e21574e86 100644 --- a/tests/ui/suggestions/closure-arg-trailing-semicolon.rs +++ b/tests/ui/suggestions/closure-arg-trailing-semicolon.rs @@ -9,9 +9,16 @@ impl Bar for u8 {} //~^ HELP the trait `Bar` is implemented for `u8` //~| HELP the trait `Bar` is implemented for `u8` //~| HELP the trait `Bar` is implemented for `u8` +//~| HELP the trait `Bar` is implemented for `u8` fn bar(_: impl Fn() -> R) {} +fn two(_: impl Fn() -> (), _: impl Fn() -> R) {} + +fn unrelated(_: impl Fn() -> ()) -> R { + loop {} +} + struct S; impl S { fn run(&self, _: impl Fn() -> R) {} @@ -42,4 +49,14 @@ fn main() { // No suggestion: the closure body is empty. bar(|| {}); //~^ ERROR the trait bound `(): Bar` is not satisfied + + // Only the second closure returns the type the failing bound is on. + two(|| { 5u8; }, || { 7u8; }); + //~^ ERROR the trait bound `(): Bar` is not satisfied + //~| HELP remove this semicolon + + // No suggestion: `R` is the return type of `unrelated` and is inferred as `()` from the + // expected type, so keeping the closure's value would not satisfy the bound. + let _: () = unrelated(|| { 5u8; }); + //~^ ERROR the trait bound `(): Bar` is not satisfied } diff --git a/tests/ui/suggestions/closure-arg-trailing-semicolon.stderr b/tests/ui/suggestions/closure-arg-trailing-semicolon.stderr index edcf95255de5d..8f4516c896a83 100644 --- a/tests/ui/suggestions/closure-arg-trailing-semicolon.stderr +++ b/tests/ui/suggestions/closure-arg-trailing-semicolon.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/closure-arg-trailing-semicolon.rs:21:5 + --> $DIR/closure-arg-trailing-semicolon.rs:28:5 | LL | bar(|| { 5u8; }); | ^^^^^^^^^----^^^ @@ -9,13 +9,13 @@ LL | bar(|| { 5u8; }); | the trait `Bar` is not implemented for `()` | note: required by a bound in `bar` - --> $DIR/closure-arg-trailing-semicolon.rs:13:11 + --> $DIR/closure-arg-trailing-semicolon.rs:14:11 | LL | fn bar(_: impl Fn() -> R) {} | ^^^ required by this bound in `bar` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/closure-arg-trailing-semicolon.rs:25:7 + --> $DIR/closure-arg-trailing-semicolon.rs:32:7 | LL | S.run(|| { 5u8; }); | ^^^ ---- help: remove this semicolon @@ -24,13 +24,13 @@ LL | S.run(|| { 5u8; }); | the trait `Bar` is not implemented for `()` | note: required by a bound in `S::run` - --> $DIR/closure-arg-trailing-semicolon.rs:17:15 + --> $DIR/closure-arg-trailing-semicolon.rs:24:15 | LL | fn run(&self, _: impl Fn() -> R) {} | ^^^ required by this bound in `S::run` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/closure-arg-trailing-semicolon.rs:31:5 + --> $DIR/closure-arg-trailing-semicolon.rs:38:5 | LL | let c = || { 5u8; }; | ---- help: remove this semicolon @@ -41,13 +41,13 @@ LL | bar(c); | ^^^^^^ the trait `Bar` is not implemented for `()` | note: required by a bound in `bar` - --> $DIR/closure-arg-trailing-semicolon.rs:13:11 + --> $DIR/closure-arg-trailing-semicolon.rs:14:11 | LL | fn bar(_: impl Fn() -> R) {} | ^^^ required by this bound in `bar` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/closure-arg-trailing-semicolon.rs:35:5 + --> $DIR/closure-arg-trailing-semicolon.rs:42:5 | LL | bar(|| { fn why() {} }); | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `()` @@ -58,13 +58,13 @@ help: the trait `Bar` is implemented for `u8` LL | impl Bar for u8 {} | ^^^^^^^^^^^^^^^ note: required by a bound in `bar` - --> $DIR/closure-arg-trailing-semicolon.rs:13:11 + --> $DIR/closure-arg-trailing-semicolon.rs:14:11 | LL | fn bar(_: impl Fn() -> R) {} | ^^^ required by this bound in `bar` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/closure-arg-trailing-semicolon.rs:39:5 + --> $DIR/closure-arg-trailing-semicolon.rs:46:5 | LL | bar(|| { "x"; }); | ^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `()` @@ -75,13 +75,13 @@ help: the trait `Bar` is implemented for `u8` LL | impl Bar for u8 {} | ^^^^^^^^^^^^^^^ note: required by a bound in `bar` - --> $DIR/closure-arg-trailing-semicolon.rs:13:11 + --> $DIR/closure-arg-trailing-semicolon.rs:14:11 | LL | fn bar(_: impl Fn() -> R) {} | ^^^ required by this bound in `bar` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/closure-arg-trailing-semicolon.rs:43:5 + --> $DIR/closure-arg-trailing-semicolon.rs:50:5 | LL | bar(|| {}); | ^^^^^^^^^^ the trait `Bar` is not implemented for `()` @@ -92,11 +92,44 @@ help: the trait `Bar` is implemented for `u8` LL | impl Bar for u8 {} | ^^^^^^^^^^^^^^^ note: required by a bound in `bar` - --> $DIR/closure-arg-trailing-semicolon.rs:13:11 + --> $DIR/closure-arg-trailing-semicolon.rs:14:11 | LL | fn bar(_: impl Fn() -> R) {} | ^^^ required by this bound in `bar` -error: aborting due to 6 previous errors +error[E0277]: the trait bound `(): Bar` is not satisfied + --> $DIR/closure-arg-trailing-semicolon.rs:54:5 + | +LL | two(|| { 5u8; }, || { 7u8; }); + | ^^^^^^^^^^^^^^^^^^^^^^----^^^ + | | | | + | | | help: remove this semicolon + | | this expression has type `u8`, which implements `Bar` + | the trait `Bar` is not implemented for `()` + | +note: required by a bound in `two` + --> $DIR/closure-arg-trailing-semicolon.rs:16:11 + | +LL | fn two(_: impl Fn() -> (), _: impl Fn() -> R) {} + | ^^^ required by this bound in `two` + +error[E0277]: the trait bound `(): Bar` is not satisfied + --> $DIR/closure-arg-trailing-semicolon.rs:60:17 + | +LL | let _: () = unrelated(|| { 5u8; }); + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `()` + | +help: the trait `Bar` is implemented for `u8` + --> $DIR/closure-arg-trailing-semicolon.rs:8:1 + | +LL | impl Bar for u8 {} + | ^^^^^^^^^^^^^^^ +note: required by a bound in `unrelated` + --> $DIR/closure-arg-trailing-semicolon.rs:18:17 + | +LL | fn unrelated(_: impl Fn() -> ()) -> R { + | ^^^ required by this bound in `unrelated` + +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0277`. From f76367d6622c8f6750cf7eafd6d8c3974c7eb53e Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Mon, 24 Aug 2026 09:04:02 +0600 Subject: [PATCH 9/9] dont call fn_sig unless the callee is a function the WhereClauseInExpr cause code carries the def id of any item named in an expression, not only a called function, so the closure lookup could reach fn_sig with an associated const or a type alias and ICE. bail out unless the def kind is a function. rename the shared emitter to emit_semicolon_removal_suggestion so it does not read like the suggest_semicolon_removal entry point, and follow the ty::PolyTraitPredicate to ty::PolyTraitClause rename from master. --- .../src/error_reporting/traits/suggestions.rs | 17 +++--- .../closure-arg-trailing-semicolon.rs | 14 +++++ .../closure-arg-trailing-semicolon.stderr | 53 +++++++++++++------ 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 9d89db381d17f..593bd9588673f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2549,7 +2549,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { // A function body has a single return type, so keeping the value can't break any // other use of it. - self.suggest_removing_semicolon( + self.emit_semicolon_removal_suggestion( err, trait_pred, candidate, @@ -2566,7 +2566,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, block: &hir::Block<'tcx>, obligation: &PredicateObligation<'tcx>, - trait_pred: ty::PolyTraitPredicate<'tcx>, + trait_pred: ty::PolyTraitClause<'tcx>, ) -> Option<(&'tcx hir::Stmt<'tcx>, &'tcx hir::Expr<'tcx>, Ty<'tcx>)> { if block.expr.is_none() && let Some(stmt) = block.stmts.last() @@ -2587,10 +2587,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_removing_semicolon( + fn emit_semicolon_removal_suggestion( &self, err: &mut Diag<'_>, - trait_pred: ty::PolyTraitPredicate<'tcx>, + trait_pred: ty::PolyTraitClause<'tcx>, (stmt, expr, ty): (&hir::Stmt<'_>, &hir::Expr<'_>, Ty<'tcx>), applicability: Applicability, ) { @@ -2622,13 +2622,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, - trait_pred: ty::PolyTraitPredicate<'tcx>, + trait_pred: ty::PolyTraitClause<'tcx>, ) -> bool { let &ObligationCauseCode::WhereClauseInExpr(callee_def_id, _, hir_id, idx) = obligation.cause.code().peel_derives() else { return false; }; + // This cause code is used for the clauses of any item named in an expression, like an + // associated const or a type alias, and `fn_sig` is only defined for functions. + if !matches!(self.tcx.def_kind(callee_def_id), DefKind::Fn | DefKind::AssocFn) { + return false; + } let hir::Node::Expr(expr) = self.tcx.hir_node(hir_id) else { return false; }; @@ -2707,7 +2712,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { // The same closure can be passed to somewhere else that expects it to return `()`, // where keeping the value would introduce a new error. - self.suggest_removing_semicolon( + self.emit_semicolon_removal_suggestion( err, trait_pred, candidate, diff --git a/tests/ui/suggestions/closure-arg-trailing-semicolon.rs b/tests/ui/suggestions/closure-arg-trailing-semicolon.rs index 3c37e21574e86..de5ecd1ddaa3a 100644 --- a/tests/ui/suggestions/closure-arg-trailing-semicolon.rs +++ b/tests/ui/suggestions/closure-arg-trailing-semicolon.rs @@ -10,6 +10,7 @@ impl Bar for u8 {} //~| HELP the trait `Bar` is implemented for `u8` //~| HELP the trait `Bar` is implemented for `u8` //~| HELP the trait `Bar` is implemented for `u8` +//~| HELP the trait `Bar` is implemented for `u8` fn bar(_: impl Fn() -> R) {} @@ -24,6 +25,14 @@ impl S { fn run(&self, _: impl Fn() -> R) {} } +trait Callable { + const CALL: fn(); +} + +impl Callable for () { + const CALL: fn() = || {}; +} + fn main() { bar(|| { 5u8; }); //~^ ERROR the trait bound `(): Bar` is not satisfied @@ -59,4 +68,9 @@ fn main() { // expected type, so keeping the closure's value would not satisfy the bound. let _: () = unrelated(|| { 5u8; }); //~^ ERROR the trait bound `(): Bar` is not satisfied + + // No suggestion: the failing clause belongs to an associated const, which has no signature to + // match a closure argument against. + <() as Callable<()>>::CALL(); + //~^ ERROR the trait bound `(): Bar` is not satisfied } diff --git a/tests/ui/suggestions/closure-arg-trailing-semicolon.stderr b/tests/ui/suggestions/closure-arg-trailing-semicolon.stderr index 8f4516c896a83..f0f9ef83345c7 100644 --- a/tests/ui/suggestions/closure-arg-trailing-semicolon.stderr +++ b/tests/ui/suggestions/closure-arg-trailing-semicolon.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/closure-arg-trailing-semicolon.rs:28:5 + --> $DIR/closure-arg-trailing-semicolon.rs:37:5 | LL | bar(|| { 5u8; }); | ^^^^^^^^^----^^^ @@ -9,13 +9,13 @@ LL | bar(|| { 5u8; }); | the trait `Bar` is not implemented for `()` | note: required by a bound in `bar` - --> $DIR/closure-arg-trailing-semicolon.rs:14:11 + --> $DIR/closure-arg-trailing-semicolon.rs:15:11 | LL | fn bar(_: impl Fn() -> R) {} | ^^^ required by this bound in `bar` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/closure-arg-trailing-semicolon.rs:32:7 + --> $DIR/closure-arg-trailing-semicolon.rs:41:7 | LL | S.run(|| { 5u8; }); | ^^^ ---- help: remove this semicolon @@ -24,13 +24,13 @@ LL | S.run(|| { 5u8; }); | the trait `Bar` is not implemented for `()` | note: required by a bound in `S::run` - --> $DIR/closure-arg-trailing-semicolon.rs:24:15 + --> $DIR/closure-arg-trailing-semicolon.rs:25:15 | LL | fn run(&self, _: impl Fn() -> R) {} | ^^^ required by this bound in `S::run` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/closure-arg-trailing-semicolon.rs:38:5 + --> $DIR/closure-arg-trailing-semicolon.rs:47:5 | LL | let c = || { 5u8; }; | ---- help: remove this semicolon @@ -41,13 +41,13 @@ LL | bar(c); | ^^^^^^ the trait `Bar` is not implemented for `()` | note: required by a bound in `bar` - --> $DIR/closure-arg-trailing-semicolon.rs:14:11 + --> $DIR/closure-arg-trailing-semicolon.rs:15:11 | LL | fn bar(_: impl Fn() -> R) {} | ^^^ required by this bound in `bar` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/closure-arg-trailing-semicolon.rs:42:5 + --> $DIR/closure-arg-trailing-semicolon.rs:51:5 | LL | bar(|| { fn why() {} }); | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `()` @@ -58,13 +58,13 @@ help: the trait `Bar` is implemented for `u8` LL | impl Bar for u8 {} | ^^^^^^^^^^^^^^^ note: required by a bound in `bar` - --> $DIR/closure-arg-trailing-semicolon.rs:14:11 + --> $DIR/closure-arg-trailing-semicolon.rs:15:11 | LL | fn bar(_: impl Fn() -> R) {} | ^^^ required by this bound in `bar` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/closure-arg-trailing-semicolon.rs:46:5 + --> $DIR/closure-arg-trailing-semicolon.rs:55:5 | LL | bar(|| { "x"; }); | ^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `()` @@ -75,13 +75,13 @@ help: the trait `Bar` is implemented for `u8` LL | impl Bar for u8 {} | ^^^^^^^^^^^^^^^ note: required by a bound in `bar` - --> $DIR/closure-arg-trailing-semicolon.rs:14:11 + --> $DIR/closure-arg-trailing-semicolon.rs:15:11 | LL | fn bar(_: impl Fn() -> R) {} | ^^^ required by this bound in `bar` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/closure-arg-trailing-semicolon.rs:50:5 + --> $DIR/closure-arg-trailing-semicolon.rs:59:5 | LL | bar(|| {}); | ^^^^^^^^^^ the trait `Bar` is not implemented for `()` @@ -92,13 +92,13 @@ help: the trait `Bar` is implemented for `u8` LL | impl Bar for u8 {} | ^^^^^^^^^^^^^^^ note: required by a bound in `bar` - --> $DIR/closure-arg-trailing-semicolon.rs:14:11 + --> $DIR/closure-arg-trailing-semicolon.rs:15:11 | LL | fn bar(_: impl Fn() -> R) {} | ^^^ required by this bound in `bar` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/closure-arg-trailing-semicolon.rs:54:5 + --> $DIR/closure-arg-trailing-semicolon.rs:63:5 | LL | two(|| { 5u8; }, || { 7u8; }); | ^^^^^^^^^^^^^^^^^^^^^^----^^^ @@ -108,13 +108,13 @@ LL | two(|| { 5u8; }, || { 7u8; }); | the trait `Bar` is not implemented for `()` | note: required by a bound in `two` - --> $DIR/closure-arg-trailing-semicolon.rs:16:11 + --> $DIR/closure-arg-trailing-semicolon.rs:17:11 | LL | fn two(_: impl Fn() -> (), _: impl Fn() -> R) {} | ^^^ required by this bound in `two` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/closure-arg-trailing-semicolon.rs:60:17 + --> $DIR/closure-arg-trailing-semicolon.rs:69:17 | LL | let _: () = unrelated(|| { 5u8; }); | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `()` @@ -125,11 +125,30 @@ help: the trait `Bar` is implemented for `u8` LL | impl Bar for u8 {} | ^^^^^^^^^^^^^^^ note: required by a bound in `unrelated` - --> $DIR/closure-arg-trailing-semicolon.rs:18:17 + --> $DIR/closure-arg-trailing-semicolon.rs:19:17 | LL | fn unrelated(_: impl Fn() -> ()) -> R { | ^^^ required by this bound in `unrelated` -error: aborting due to 8 previous errors +error[E0277]: the trait bound `(): Bar` is not satisfied + --> $DIR/closure-arg-trailing-semicolon.rs:74:21 + | +LL | <() as Callable<()>>::CALL(); + | ^^ the trait `Bar` is not implemented for `()` + | +help: the trait `Bar` is implemented for `u8` + --> $DIR/closure-arg-trailing-semicolon.rs:8:1 + | +LL | impl Bar for u8 {} + | ^^^^^^^^^^^^^^^ +note: required by a bound in `Callable::CALL` + --> $DIR/closure-arg-trailing-semicolon.rs:28:19 + | +LL | trait Callable { + | ^^^ required by this bound in `Callable::CALL` +LL | const CALL: fn(); + | ---- required by a bound in this associated constant + +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0277`.