From 114dd2061e0bfa6bc1353d0265389cfaa04d8858 Mon Sep 17 00:00:00 2001 From: Alan Egerton Date: Wed, 12 Jun 2024 13:01:22 +0100 Subject: [PATCH 01/11] Un-unsafe the `StableOrd` trait Whilst incorrect implementations of this trait can cause miscompilation, they cannot cause memory unsafety in rustc. --- compiler/rustc_abi/src/lib.rs | 4 +-- .../src/stable_hasher.rs | 33 ++++++++++--------- compiler/rustc_hir/src/hir_id.rs | 4 +-- .../src/dep_graph/dep_node.rs | 2 +- compiler/rustc_session/src/config.rs | 4 +-- compiler/rustc_span/src/def_id.rs | 4 +-- 6 files changed, 26 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index b1a17d5a24b6..5d8c882803d3 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -425,10 +425,10 @@ pub struct Size { raw: u64, } -// Safety: Ord is implement as just comparing numerical values and numerical values +// Ord is implement as just comparing numerical values and numerical values // are not changed by (de-)serialization. #[cfg(feature = "nightly")] -unsafe impl StableOrd for Size { +impl StableOrd for Size { const CAN_USE_UNSTABLE_SORT: bool = true; } diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index b5bdf2e17904..206146cc60dd 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -238,11 +238,14 @@ pub trait ToStableHashKey { /// The associated constant `CAN_USE_UNSTABLE_SORT` denotes whether /// unstable sorting can be used for this type. Set to true if and /// only if `a == b` implies `a` and `b` are fully indistinguishable. -pub unsafe trait StableOrd: Ord { +/// +/// **Be careful when implementing this trait, as an incorrect +/// implementation can cause miscompilation!** +pub trait StableOrd: Ord { const CAN_USE_UNSTABLE_SORT: bool; } -unsafe impl StableOrd for &T { +impl StableOrd for &T { const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT; } @@ -290,7 +293,7 @@ macro_rules! impl_stable_traits_for_trivial_type { } } - unsafe impl $crate::stable_hasher::StableOrd for $t { + impl $crate::stable_hasher::StableOrd for $t { const CAN_USE_UNSTABLE_SORT: bool = true; } }; @@ -327,7 +330,7 @@ impl HashStable for Hash128 { } } -unsafe impl StableOrd for Hash128 { +impl StableOrd for Hash128 { const CAN_USE_UNSTABLE_SORT: bool = true; } @@ -392,7 +395,7 @@ impl, T2: HashStable, CTX> HashStable for (T1, T2) } } -unsafe impl StableOrd for (T1, T2) { +impl StableOrd for (T1, T2) { const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT; } @@ -410,7 +413,7 @@ where } } -unsafe impl StableOrd for (T1, T2, T3) { +impl StableOrd for (T1, T2, T3) { const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT && T3::CAN_USE_UNSTABLE_SORT; } @@ -431,9 +434,7 @@ where } } -unsafe impl StableOrd - for (T1, T2, T3, T4) -{ +impl StableOrd for (T1, T2, T3, T4) { const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT && T3::CAN_USE_UNSTABLE_SORT @@ -530,7 +531,7 @@ impl HashStable for str { } } -unsafe impl StableOrd for &str { +impl StableOrd for &str { const CAN_USE_UNSTABLE_SORT: bool = true; } @@ -541,9 +542,9 @@ impl HashStable for String { } } -// Safety: String comparison only depends on their contents and the +// String comparison only depends on their contents and the // contents are not changed by (de-)serialization. -unsafe impl StableOrd for String { +impl StableOrd for String { const CAN_USE_UNSTABLE_SORT: bool = true; } @@ -570,8 +571,8 @@ impl HashStable for bool { } } -// Safety: sort order of bools is not changed by (de-)serialization. -unsafe impl StableOrd for bool { +// sort order of bools is not changed by (de-)serialization. +impl StableOrd for bool { const CAN_USE_UNSTABLE_SORT: bool = true; } @@ -590,8 +591,8 @@ where } } -// Safety: the Option wrapper does not add instability to comparison. -unsafe impl StableOrd for Option { +// the Option wrapper does not add instability to comparison. +impl StableOrd for Option { const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT; } diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs index ac4874695070..1ed84fb09683 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs @@ -165,9 +165,9 @@ impl ItemLocalId { pub const INVALID: ItemLocalId = ItemLocalId::MAX; } -// Safety: Ord is implement as just comparing the ItemLocalId's numerical +// Ord is implement as just comparing the ItemLocalId's numerical // values and these are not changed by (de-)serialization. -unsafe impl StableOrd for ItemLocalId { +impl StableOrd for ItemLocalId { const CAN_USE_UNSTABLE_SORT: bool = true; } diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index 5f1a03502a70..3fb59ad26f62 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -301,7 +301,7 @@ impl ToStableHashKey for WorkProductId { self.hash } } -unsafe impl StableOrd for WorkProductId { +impl StableOrd for WorkProductId { // Fingerprint can use unstable (just a tuple of `u64`s), so WorkProductId can as well const CAN_USE_UNSTABLE_SORT: bool = true; } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index a622f1b577df..d5428df03290 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -491,8 +491,8 @@ pub enum OutputType { DepInfo, } -// Safety: Trivial C-Style enums have a stable sort order across compilation sessions. -unsafe impl StableOrd for OutputType { +// Trivial C-Style enums have a stable sort order across compilation sessions. +impl StableOrd for OutputType { const CAN_USE_UNSTABLE_SORT: bool = true; } diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index 1ac3a817bba5..4d534ad80071 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -120,8 +120,8 @@ impl Default for DefPathHash { } } -// Safety: `DefPathHash` sort order is not affected (de)serialization. -unsafe impl StableOrd for DefPathHash { +// `DefPathHash` sort order is not affected (de)serialization. +impl StableOrd for DefPathHash { const CAN_USE_UNSTABLE_SORT: bool = true; } From a264bff9d557e9fc468c9fefeabc0c09d4eeea6f Mon Sep 17 00:00:00 2001 From: mu001999 Date: Tue, 18 Jun 2024 15:56:34 +0800 Subject: [PATCH 02/11] Mark assoc tys live only if the trait is live --- compiler/rustc_passes/src/dead.rs | 41 +++++++++++-------- .../ui/const-generics/cross_crate_complex.rs | 1 + .../missing-bounds.fixed | 4 ++ .../missing-bounds.rs | 4 ++ .../missing-bounds.stderr | 18 ++++---- .../dead-code/unused-trait-with-assoc-ty.rs | 11 +++++ .../unused-trait-with-assoc-ty.stderr | 20 +++++++++ tests/ui/pattern/issue-22546.rs | 2 +- tests/ui/pattern/issue-22546.stderr | 10 ----- 9 files changed, 75 insertions(+), 36 deletions(-) create mode 100644 tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs create mode 100644 tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr delete mode 100644 tests/ui/pattern/issue-22546.stderr diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 6a74ddc5508a..7c8c1d251ae0 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -156,7 +156,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { fn handle_res(&mut self, res: Res) { match res { - Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias, def_id) => { + Res::Def( + DefKind::Const | DefKind::AssocConst | DefKind::AssocTy | DefKind::TyAlias, + def_id, + ) => { self.check_def_id(def_id); } _ if self.in_pat => {} @@ -442,7 +445,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { intravisit::walk_item(self, item) } hir::ItemKind::ForeignMod { .. } => {} - hir::ItemKind::Trait(..) => { + hir::ItemKind::Trait(_, _, _, _, trait_item_refs) => { for impl_def_id in self.tcx.all_impls(item.owner_id.to_def_id()) { if let Some(local_def_id) = impl_def_id.as_local() && let ItemKind::Impl(impl_ref) = @@ -455,7 +458,12 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { intravisit::walk_path(self, impl_ref.of_trait.unwrap().path); } } - + // mark assoc ty live if the trait is live + for trait_item in trait_item_refs { + if let hir::AssocItemKind::Type = trait_item.kind { + self.check_def_id(trait_item.id.owner_id.to_def_id()); + } + } intravisit::walk_item(self, item) } _ => intravisit::walk_item(self, item), @@ -472,9 +480,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { && let ItemKind::Impl(impl_ref) = self.tcx.hir().expect_item(local_impl_id).kind { - if !matches!(trait_item.kind, hir::TraitItemKind::Type(..)) - && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty) - .ty_and_all_fields_are_public + if !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty) + .ty_and_all_fields_are_public { // skip impl-items of non pure pub ty, // cause we don't know the ty is constructed or not, @@ -813,9 +820,8 @@ fn check_item<'tcx>( // for trait impl blocks, // mark the method live if the self_ty is public, // or the method is public and may construct self - if of_trait && matches!(tcx.def_kind(local_def_id), DefKind::AssocTy) - || tcx.visibility(local_def_id).is_public() - && (ty_and_all_fields_are_public || may_construct_self) + if tcx.visibility(local_def_id).is_public() + && (ty_and_all_fields_are_public || may_construct_self) { // if the impl item is public, // and the ty may be constructed or can be constructed in foreign crates, @@ -852,10 +858,13 @@ fn check_trait_item( worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>, id: hir::TraitItemId, ) { - use hir::TraitItemKind::{Const, Fn}; - if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) { + use hir::TraitItemKind::{Const, Fn, Type}; + if matches!( + tcx.def_kind(id.owner_id), + DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn + ) { let trait_item = tcx.hir().trait_item(id); - if matches!(trait_item.kind, Const(_, Some(_)) | Fn(..)) + if matches!(trait_item.kind, Const(_, Some(_)) | Type(_, Some(_)) | Fn(..)) && let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id) { @@ -897,7 +906,7 @@ fn create_and_seed_worklist( // checks impls, impl-items and pub structs with all public fields later match tcx.def_kind(id) { DefKind::Impl { .. } => false, - DefKind::AssocConst | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), + DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), _ => true }) @@ -1132,6 +1141,7 @@ impl<'tcx> DeadVisitor<'tcx> { } match self.tcx.def_kind(def_id) { DefKind::AssocConst + | DefKind::AssocTy | DefKind::AssocFn | DefKind::Fn | DefKind::Static { .. } @@ -1173,15 +1183,14 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { || (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id)) { for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) { - // We have diagnosed unused assoc consts and fns in traits + // We have diagnosed unused assocs in traits if matches!(def_kind, DefKind::Impl { of_trait: true }) - && matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocFn) + && matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn) // skip unused public inherent methods, // cause we have diagnosed unconstructed struct || matches!(def_kind, DefKind::Impl { of_trait: false }) && tcx.visibility(def_id).is_public() && ty_ref_to_pub_struct(tcx, tcx.hir().item(item).expect_impl().self_ty).ty_is_public - || def_kind == DefKind::Trait && tcx.def_kind(def_id) == DefKind::AssocTy { continue; } diff --git a/tests/ui/const-generics/cross_crate_complex.rs b/tests/ui/const-generics/cross_crate_complex.rs index d13b69aa0cfb..b44d889f5e99 100644 --- a/tests/ui/const-generics/cross_crate_complex.rs +++ b/tests/ui/const-generics/cross_crate_complex.rs @@ -11,6 +11,7 @@ async fn foo() { async_in_foo(async_out_foo::<4>().await).await; } +#[allow(dead_code)] struct Faz; impl Foo for Faz {} diff --git a/tests/ui/generic-associated-types/missing-bounds.fixed b/tests/ui/generic-associated-types/missing-bounds.fixed index 703d3c1e0fb1..ff69016d8626 100644 --- a/tests/ui/generic-associated-types/missing-bounds.fixed +++ b/tests/ui/generic-associated-types/missing-bounds.fixed @@ -2,6 +2,7 @@ use std::ops::Add; +#[allow(dead_code)] struct A(B); impl Add for A where B: Add { @@ -12,6 +13,7 @@ impl Add for A where B: Add { } } +#[allow(dead_code)] struct C(B); impl> Add for C { @@ -22,6 +24,7 @@ impl> Add for C { } } +#[allow(dead_code)] struct D(B); impl> Add for D { @@ -32,6 +35,7 @@ impl> Add for D { } } +#[allow(dead_code)] struct E(B); impl> Add for E where B: Add { diff --git a/tests/ui/generic-associated-types/missing-bounds.rs b/tests/ui/generic-associated-types/missing-bounds.rs index f40b42288731..1f83356c2fa6 100644 --- a/tests/ui/generic-associated-types/missing-bounds.rs +++ b/tests/ui/generic-associated-types/missing-bounds.rs @@ -2,6 +2,7 @@ use std::ops::Add; +#[allow(dead_code)] struct A(B); impl Add for A where B: Add { @@ -12,6 +13,7 @@ impl Add for A where B: Add { } } +#[allow(dead_code)] struct C(B); impl Add for C { @@ -22,6 +24,7 @@ impl Add for C { } } +#[allow(dead_code)] struct D(B); impl Add for D { @@ -32,6 +35,7 @@ impl Add for D { } } +#[allow(dead_code)] struct E(B); impl Add for E where ::Output = B { diff --git a/tests/ui/generic-associated-types/missing-bounds.stderr b/tests/ui/generic-associated-types/missing-bounds.stderr index 1d7d80d1b076..0f0dc24c06c0 100644 --- a/tests/ui/generic-associated-types/missing-bounds.stderr +++ b/tests/ui/generic-associated-types/missing-bounds.stderr @@ -1,5 +1,5 @@ error: equality constraints are not yet supported in `where` clauses - --> $DIR/missing-bounds.rs:37:33 + --> $DIR/missing-bounds.rs:41:33 | LL | impl Add for E where ::Output = B { | ^^^^^^^^^^^^^^^^^^^^^^ not supported @@ -11,7 +11,7 @@ LL | impl Add for E where B: Add { | ~~~~~~~~~~~~~~~~~~ error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:11:11 + --> $DIR/missing-bounds.rs:12:11 | LL | impl Add for A where B: Add { | - expected this type parameter @@ -24,14 +24,14 @@ LL | A(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` help: the type constructed contains `::Output` due to the type of the argument passed - --> $DIR/missing-bounds.rs:11:9 + --> $DIR/missing-bounds.rs:12:9 | LL | A(self.0 + rhs.0) | ^^--------------^ | | | this argument influences the type of `A` note: tuple struct defined here - --> $DIR/missing-bounds.rs:5:8 + --> $DIR/missing-bounds.rs:6:8 | LL | struct A(B); | ^ @@ -41,7 +41,7 @@ LL | impl Add for A where B: Add { | ++++++++++++ error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:21:14 + --> $DIR/missing-bounds.rs:23:14 | LL | impl Add for C { | - expected this type parameter @@ -54,7 +54,7 @@ LL | Self(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` note: tuple struct defined here - --> $DIR/missing-bounds.rs:15:8 + --> $DIR/missing-bounds.rs:17:8 | LL | struct C(B); | ^ @@ -64,7 +64,7 @@ LL | impl> Add for C { | ++++++++++++ error[E0369]: cannot add `B` to `B` - --> $DIR/missing-bounds.rs:31:21 + --> $DIR/missing-bounds.rs:34:21 | LL | Self(self.0 + rhs.0) | ------ ^ ----- B @@ -77,7 +77,7 @@ LL | impl> Add for D { | +++++++++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:42:14 + --> $DIR/missing-bounds.rs:46:14 | LL | impl Add for E where ::Output = B { | - expected this type parameter @@ -90,7 +90,7 @@ LL | Self(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` note: tuple struct defined here - --> $DIR/missing-bounds.rs:35:8 + --> $DIR/missing-bounds.rs:39:8 | LL | struct E(B); | ^ diff --git a/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs new file mode 100644 index 000000000000..e8116d83ebf1 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs @@ -0,0 +1,11 @@ +#![deny(dead_code)] + +struct T1; //~ ERROR struct `T1` is never constructed + +trait Foo { type Unused; } //~ ERROR trait `Foo` is never used +impl Foo for T1 { type Unused = Self; } + +pub trait Bar { type Used; } +impl Bar for T1 { type Used = Self; } + +fn main() {} diff --git a/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr new file mode 100644 index 000000000000..ab73c6406343 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr @@ -0,0 +1,20 @@ +error: struct `T1` is never constructed + --> $DIR/unused-trait-with-assoc-ty.rs:3:8 + | +LL | struct T1; + | ^^ + | +note: the lint level is defined here + --> $DIR/unused-trait-with-assoc-ty.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: trait `Foo` is never used + --> $DIR/unused-trait-with-assoc-ty.rs:5:7 + | +LL | trait Foo { type Unused; } + | ^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/pattern/issue-22546.rs b/tests/ui/pattern/issue-22546.rs index fd1d5fb6c477..d5c5b68be78d 100644 --- a/tests/ui/pattern/issue-22546.rs +++ b/tests/ui/pattern/issue-22546.rs @@ -15,7 +15,7 @@ impl Foo { } } -trait Tr { //~ WARN trait `Tr` is never used +trait Tr { type U; } diff --git a/tests/ui/pattern/issue-22546.stderr b/tests/ui/pattern/issue-22546.stderr deleted file mode 100644 index e067a95e4226..000000000000 --- a/tests/ui/pattern/issue-22546.stderr +++ /dev/null @@ -1,10 +0,0 @@ -warning: trait `Tr` is never used - --> $DIR/issue-22546.rs:18:7 - | -LL | trait Tr { - | ^^ - | - = note: `#[warn(dead_code)]` on by default - -warning: 1 warning emitted - From dd557d8c3720c82c488d6e2af3b7e19025f2473a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 20 Jun 2024 12:04:59 -0400 Subject: [PATCH 03/11] Add a test demonstrating that RPITITs cant use precise capturing --- .../ui/impl-trait/precise-capturing/rpitit.rs | 21 ++++++++++ .../precise-capturing/rpitit.stderr | 42 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 tests/ui/impl-trait/precise-capturing/rpitit.rs create mode 100644 tests/ui/impl-trait/precise-capturing/rpitit.stderr diff --git a/tests/ui/impl-trait/precise-capturing/rpitit.rs b/tests/ui/impl-trait/precise-capturing/rpitit.rs new file mode 100644 index 000000000000..4eb053573e1f --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/rpitit.rs @@ -0,0 +1,21 @@ +//@ known-bug: unknown + +// RPITITs don't have variances in their GATs, so they always relate invariantly +// and act as if they capture all their args. +// To fix this soundly, we need to make sure that all the trait header args +// remain captured, since they affect trait selection. + +#![feature(precise_capturing)] + +trait Foo<'a> { + fn hello() -> impl PartialEq + use; +} + +fn test<'a, 'b, T: for<'r> Foo<'r>>() { + PartialEq::eq( + &>::hello(), + &>::hello(), + ); +} + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/rpitit.stderr b/tests/ui/impl-trait/precise-capturing/rpitit.stderr new file mode 100644 index 000000000000..202eeb39385d --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/rpitit.stderr @@ -0,0 +1,42 @@ +error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list + --> $DIR/rpitit.rs:11:19 + | +LL | trait Foo<'a> { + | -- this lifetime parameter is captured +LL | fn hello() -> impl PartialEq + use; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime captured due to being mentioned in the bounds of the `impl Trait` + +error: lifetime may not live long enough + --> $DIR/rpitit.rs:15:5 + | +LL | fn test<'a, 'b, T: for<'r> Foo<'r>>() { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | / PartialEq::eq( +LL | | &>::hello(), +LL | | &>::hello(), +LL | | ); + | |_____^ argument requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/rpitit.rs:15:5 + | +LL | fn test<'a, 'b, T: for<'r> Foo<'r>>() { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | / PartialEq::eq( +LL | | &>::hello(), +LL | | &>::hello(), +LL | | ); + | |_____^ argument requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +help: `'a` and `'b` must be the same: replace one with the other + +error: aborting due to 3 previous errors + From c39b86a687df74a6b1157408eaf801f9123ccf58 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 17 Jun 2024 18:47:09 +1000 Subject: [PATCH 04/11] Fix a span in `parse_ty_bare_fn`. It currently goes one token too far. Example: line 259 of `tests/ui/abi/compatibility.rs`: ``` test_abi_compatible!(fn_fn, fn(), fn(i32) -> i32); ``` This commit changes the span for the second element from `fn(),` to `fn()`, i.e. removes the extraneous comma. This doesn't affect any tests. I found it while debugging some other code. Not a big deal but an easy fix so I figure it worth doing. --- compiler/rustc_ast/src/ast.rs | 3 ++- compiler/rustc_parse/src/parser/ty.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 71932f02017c..32d28d098492 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2126,7 +2126,8 @@ pub struct BareFnTy { pub ext: Extern, pub generic_params: ThinVec, pub decl: P, - /// Span of the `fn(...) -> ...` part. + /// Span of the `[unsafe] [extern] fn(...) -> ...` part, i.e. everything + /// after the generic params (if there are any, e.g. `for<'a>`). pub decl_span: Span, } diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index fcd623b477f5..d2043c353fed 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -608,7 +608,7 @@ impl<'a> Parser<'a> { self.dcx().emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span }); } // FIXME(gen_blocks): emit a similar error for `gen fn()` - let decl_span = span_start.to(self.token.span); + let decl_span = span_start.to(self.prev_token.span); Ok(TyKind::BareFn(P(BareFnTy { ext, safety, generic_params: params, decl, decl_span }))) } From 0e73e7095ae7aa7ead69c4ba7ba002560ef118fa Mon Sep 17 00:00:00 2001 From: Alan Egerton Date: Sat, 22 Jun 2024 07:11:42 +0100 Subject: [PATCH 05/11] Ensure careful consideration is given by impls Added an associated `const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED` to the `StableOrd` trait to ensure that implementors carefully consider whether the trait's contract is upheld, as incorrect implementations can cause miscompilations. --- compiler/rustc_abi/src/lib.rs | 6 ++- .../src/stable_hasher.rs | 50 ++++++++++++++++--- compiler/rustc_hir/src/hir_id.rs | 6 ++- .../src/dep_graph/dep_node.rs | 3 ++ compiler/rustc_session/src/config.rs | 4 +- compiler/rustc_span/src/def_id.rs | 4 +- 6 files changed, 60 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 5d8c882803d3..6aa5ad599665 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -425,11 +425,13 @@ pub struct Size { raw: u64, } -// Ord is implement as just comparing numerical values and numerical values -// are not changed by (de-)serialization. #[cfg(feature = "nightly")] impl StableOrd for Size { const CAN_USE_UNSTABLE_SORT: bool = true; + + // `Ord` is implemented as just comparing numerical values and numerical values + // are not changed by (de-)serialization. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } // This is debug-printed a lot in larger structs, don't waste too much space there diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 206146cc60dd..a57f5067dd8d 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -238,15 +238,21 @@ pub trait ToStableHashKey { /// The associated constant `CAN_USE_UNSTABLE_SORT` denotes whether /// unstable sorting can be used for this type. Set to true if and /// only if `a == b` implies `a` and `b` are fully indistinguishable. -/// -/// **Be careful when implementing this trait, as an incorrect -/// implementation can cause miscompilation!** pub trait StableOrd: Ord { const CAN_USE_UNSTABLE_SORT: bool; + + /// Marker to ensure that implementors have carefully considered + /// whether their `Ord` implementation obeys this trait's contract. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: (); } impl StableOrd for &T { const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT; + + // Ordering of a reference is exactly that of the referent, and since + // the ordering of the referet is stable so must be the ordering of the + // reference. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } /// This is a companion trait to `StableOrd`. Some types like `Symbol` can be @@ -295,6 +301,10 @@ macro_rules! impl_stable_traits_for_trivial_type { impl $crate::stable_hasher::StableOrd for $t { const CAN_USE_UNSTABLE_SORT: bool = true; + + // Encoding and decoding doesn't change the bytes of trivial types + // and `Ord::cmp` depends only on those bytes. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } }; } @@ -332,6 +342,10 @@ impl HashStable for Hash128 { impl StableOrd for Hash128 { const CAN_USE_UNSTABLE_SORT: bool = true; + + // Encoding and decoding doesn't change the bytes of `Hash128` + // and `Ord::cmp` depends only on those bytes. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } impl HashStable for ! { @@ -397,6 +411,10 @@ impl, T2: HashStable, CTX> HashStable for (T1, T2) impl StableOrd for (T1, T2) { const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT; + + // Ordering of tuples is a pure function of their elements' ordering, and since + // the ordering of each element is stable so must be the ordering of the tuple. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } impl HashStable for (T1, T2, T3) @@ -416,6 +434,10 @@ where impl StableOrd for (T1, T2, T3) { const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT && T3::CAN_USE_UNSTABLE_SORT; + + // Ordering of tuples is a pure function of their elements' ordering, and since + // the ordering of each element is stable so must be the ordering of the tuple. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } impl HashStable for (T1, T2, T3, T4) @@ -439,6 +461,10 @@ impl StableOrd for ( && T2::CAN_USE_UNSTABLE_SORT && T3::CAN_USE_UNSTABLE_SORT && T4::CAN_USE_UNSTABLE_SORT; + + // Ordering of tuples is a pure function of their elements' ordering, and since + // the ordering of each element is stable so must be the ordering of the tuple. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } impl, CTX> HashStable for [T] { @@ -533,6 +559,10 @@ impl HashStable for str { impl StableOrd for &str { const CAN_USE_UNSTABLE_SORT: bool = true; + + // Encoding and decoding doesn't change the bytes of string slices + // and `Ord::cmp` depends only on those bytes. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } impl HashStable for String { @@ -542,10 +572,12 @@ impl HashStable for String { } } -// String comparison only depends on their contents and the -// contents are not changed by (de-)serialization. impl StableOrd for String { const CAN_USE_UNSTABLE_SORT: bool = true; + + // String comparison only depends on their contents and the + // contents are not changed by (de-)serialization. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } impl ToStableHashKey for String { @@ -571,9 +603,11 @@ impl HashStable for bool { } } -// sort order of bools is not changed by (de-)serialization. impl StableOrd for bool { const CAN_USE_UNSTABLE_SORT: bool = true; + + // sort order of bools is not changed by (de-)serialization. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } impl HashStable for Option @@ -591,9 +625,11 @@ where } } -// the Option wrapper does not add instability to comparison. impl StableOrd for Option { const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT; + + // the Option wrapper does not add instability to comparison. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } impl HashStable for Result diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs index 1ed84fb09683..c0ca1a8017eb 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs @@ -165,10 +165,12 @@ impl ItemLocalId { pub const INVALID: ItemLocalId = ItemLocalId::MAX; } -// Ord is implement as just comparing the ItemLocalId's numerical -// values and these are not changed by (de-)serialization. impl StableOrd for ItemLocalId { const CAN_USE_UNSTABLE_SORT: bool = true; + + // `Ord` is implemented as just comparing the ItemLocalId's numerical + // values and these are not changed by (de-)serialization. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } /// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`. diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index 3fb59ad26f62..f2a68e356715 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -304,6 +304,9 @@ impl ToStableHashKey for WorkProductId { impl StableOrd for WorkProductId { // Fingerprint can use unstable (just a tuple of `u64`s), so WorkProductId can as well const CAN_USE_UNSTABLE_SORT: bool = true; + + // `WorkProductId` sort order is not affected by (de)serialization. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } // Some types are used a lot. Make sure they don't unintentionally get bigger. diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index d5428df03290..f4fd9dee02e0 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -491,9 +491,11 @@ pub enum OutputType { DepInfo, } -// Trivial C-Style enums have a stable sort order across compilation sessions. impl StableOrd for OutputType { const CAN_USE_UNSTABLE_SORT: bool = true; + + // Trivial C-Style enums have a stable sort order across compilation sessions. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } impl ToStableHashKey for OutputType { diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index 4d534ad80071..5456303b36fe 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -120,9 +120,11 @@ impl Default for DefPathHash { } } -// `DefPathHash` sort order is not affected (de)serialization. impl StableOrd for DefPathHash { const CAN_USE_UNSTABLE_SORT: bool = true; + + // `DefPathHash` sort order is not affected by (de)serialization. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } /// A [`StableCrateId`] is a 64-bit hash of a crate name, together with all From 594fa01aba7aa48990ffb673eb6d46be239f0193 Mon Sep 17 00:00:00 2001 From: bohan Date: Sun, 23 Jun 2024 23:44:22 +0800 Subject: [PATCH 06/11] not use offset when there is not ends with brace --- compiler/rustc_hir_analysis/src/check/mod.rs | 17 +++++++--- .../missing-impl-trait-block-but-not-ascii.rs | 13 ++++++++ ...sing-impl-trait-block-but-not-ascii.stderr | 31 +++++++++++++++++++ 3 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.rs create mode 100644 tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.stderr diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 4d1b96d9c1ba..8469cbbbc7d6 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -211,11 +211,18 @@ fn missing_items_err( .collect::>() .join("`, `"); - // `Span` before impl block closing brace. - let hi = full_impl_span.hi() - BytePos(1); - // Point at the place right before the closing brace of the relevant `impl` to suggest - // adding the associated item at the end of its body. - let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi); + let sugg_sp = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(full_impl_span) + && snippet.ends_with("}") + { + // `Span` before impl block closing brace. + let hi = full_impl_span.hi() - BytePos(1); + // Point at the place right before the closing brace of the relevant `impl` to suggest + // adding the associated item at the end of its body. + full_impl_span.with_lo(hi).with_hi(hi) + } else { + full_impl_span.shrink_to_hi() + }; + // Obtain the level of indentation ending in `sugg_sp`. let padding = tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new()); diff --git a/tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.rs b/tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.rs new file mode 100644 index 000000000000..ddb6bd1e9020 --- /dev/null +++ b/tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.rs @@ -0,0 +1,13 @@ +// issue#126764 + +struct S; + +trait T { + fn f(); +} +impl T for S; +//~^ ERROR: unknown start of token +//~| ERROR: expected `{}` +//~| ERROR: not all trait items implemented, missing: `f` + +fn main() {} diff --git a/tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.stderr b/tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.stderr new file mode 100644 index 000000000000..56cdc11b62e6 --- /dev/null +++ b/tests/ui/suggestions/missing-impl-trait-block-but-not-ascii.stderr @@ -0,0 +1,31 @@ +error: unknown start of token: \u{ff1b} + --> $DIR/missing-impl-trait-block-but-not-ascii.rs:8:13 + | +LL | impl T for S; + | ^^ + | +help: Unicode character ';' (Fullwidth Semicolon) looks like ';' (Semicolon), but it is not + | +LL | impl T for S; + | ~ + +error: expected `{}`, found `;` + --> $DIR/missing-impl-trait-block-but-not-ascii.rs:8:13 + | +LL | impl T for S; + | ^^ + | + = help: try using `{}` instead + +error[E0046]: not all trait items implemented, missing: `f` + --> $DIR/missing-impl-trait-block-but-not-ascii.rs:8:1 + | +LL | fn f(); + | ------- `f` from trait +LL | } +LL | impl T for S; + | ^^^^^^^^^^^^ missing `f` in implementation + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0046`. From a2298a6f190a020da69860433b4031775ea82cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 24 Jun 2024 01:11:56 +0000 Subject: [PATCH 07/11] Do not ICE when suggesting dereferencing closure arg Account for `for` lifetimes when constructing closure to see if dereferencing the return value would be valid. Fix #125634, fix #124563. --- .../src/diagnostics/region_errors.rs | 6 ++- ...unt-for-lifetimes-in-closure-suggestion.rs | 19 +++++++ ...for-lifetimes-in-closure-suggestion.stderr | 17 +++++++ ...ough-suggestion-regression-test-124563.rs} | 11 +++-- ...h-suggestion-regression-test-124563.stderr | 49 +++++++++++++++++++ tests/ui/regions/regions-escape-method.fixed | 17 +++++++ tests/ui/regions/regions-escape-method.rs | 1 + tests/ui/regions/regions-escape-method.stderr | 7 ++- 8 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 tests/ui/regions/account-for-lifetimes-in-closure-suggestion.rs create mode 100644 tests/ui/regions/account-for-lifetimes-in-closure-suggestion.stderr rename tests/{crashes/124563.rs => ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.rs} (72%) create mode 100644 tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr create mode 100644 tests/ui/regions/regions-escape-method.fixed diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 67c11ff4a5bd..d0cdf2baf993 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1152,7 +1152,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // Get the arguments for the found method, only specifying that `Self` is the receiver type. let Some(possible_rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id) else { return }; let args = GenericArgs::for_item(tcx, method_def_id, |param, _| { - if param.index == 0 { + if let ty::GenericParamDefKind::Lifetime = param.kind { + tcx.lifetimes.re_erased.into() + } else if param.index == 0 && param.name == kw::SelfUpper { possible_rcvr_ty.into() } else if param.index == closure_param.index { closure_ty.into() @@ -1169,7 +1171,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Obligation::misc(tcx, span, self.mir_def_id(), self.param_env, pred) })); - if ocx.select_all_or_error().is_empty() { + if ocx.select_all_or_error().is_empty() && count > 0 { diag.span_suggestion_verbose( tcx.hir().body(*body).value.peel_blocks().span.shrink_to_lo(), "dereference the return value", diff --git a/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.rs b/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.rs new file mode 100644 index 000000000000..2de92cf62da1 --- /dev/null +++ b/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.rs @@ -0,0 +1,19 @@ +// #125634 +struct Thing; + +// Invariant in 'a, Covariant in 'b +struct TwoThings<'a, 'b>(*mut &'a (), &'b mut ()); + +impl Thing { + fn enter_scope<'a>(self, _scope: impl for<'b> FnOnce(TwoThings<'a, 'b>)) {} +} + +fn foo() { + Thing.enter_scope(|ctx| { + SameLifetime(ctx); //~ ERROR lifetime may not live long enough + }); +} + +struct SameLifetime<'a>(TwoThings<'a, 'a>); + +fn main() {} diff --git a/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.stderr b/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.stderr new file mode 100644 index 000000000000..5e158f59cdc6 --- /dev/null +++ b/tests/ui/regions/account-for-lifetimes-in-closure-suggestion.stderr @@ -0,0 +1,17 @@ +error: lifetime may not live long enough + --> $DIR/account-for-lifetimes-in-closure-suggestion.rs:13:22 + | +LL | Thing.enter_scope(|ctx| { + | --- + | | + | has type `TwoThings<'_, '1>` + | has type `TwoThings<'2, '_>` +LL | SameLifetime(ctx); + | ^^^ this usage requires that `'1` must outlive `'2` + | + = note: requirement occurs because of the type `TwoThings<'_, '_>`, which makes the generic argument `'_` invariant + = note: the struct `TwoThings<'a, 'b>` is invariant over the parameter `'a` + = help: see for more information about variance + +error: aborting due to 1 previous error + diff --git a/tests/crashes/124563.rs b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.rs similarity index 72% rename from tests/crashes/124563.rs rename to tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.rs index b082739af53d..23427838ceb7 100644 --- a/tests/crashes/124563.rs +++ b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.rs @@ -1,5 +1,4 @@ -//@ known-bug: rust-lang/rust#124563 - +// #124563 use std::marker::PhantomData; pub trait Trait {} @@ -17,11 +16,11 @@ where T: Trait, { type Trait = T; - type Bar = BarImpl<'a, 'b, T>; + type Bar = BarImpl<'a, 'b, T>; //~ ERROR lifetime bound not satisfied fn foo(&mut self) { - self.enter_scope(|ctx| { - BarImpl(ctx); + self.enter_scope(|ctx| { //~ ERROR lifetime may not live long enough + BarImpl(ctx); //~ ERROR lifetime may not live long enough }); } } @@ -44,3 +43,5 @@ where { type Foo = FooImpl<'a, 'b, T>; } + +fn main() {} diff --git a/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr new file mode 100644 index 000000000000..fcd0a232a7bd --- /dev/null +++ b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr @@ -0,0 +1,49 @@ +error[E0478]: lifetime bound not satisfied + --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:19:16 + | +LL | type Bar = BarImpl<'a, 'b, T>; + | ^^^^^^^^^^^^^^^^^^ + | +note: lifetime parameter instantiated with the lifetime `'a` as defined here + --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:14:6 + | +LL | impl<'a, 'b, T> Foo for FooImpl<'a, 'b, T> + | ^^ +note: but lifetime parameter must outlive the lifetime `'b` as defined here + --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:14:10 + | +LL | impl<'a, 'b, T> Foo for FooImpl<'a, 'b, T> + | ^^ + +error: lifetime may not live long enough + --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:23:21 + | +LL | self.enter_scope(|ctx| { + | --- + | | + | has type `&'1 mut FooImpl<'_, '_, T>` + | has type `&mut FooImpl<'2, '_, T>` +LL | BarImpl(ctx); + | ^^^ this usage requires that `'1` must outlive `'2` + +error: lifetime may not live long enough + --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:22:9 + | +LL | impl<'a, 'b, T> Foo for FooImpl<'a, 'b, T> + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | / self.enter_scope(|ctx| { +LL | | BarImpl(ctx); +LL | | }); + | |__________^ argument requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a mutable reference to `FooImpl<'_, '_, T>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0478`. diff --git a/tests/ui/regions/regions-escape-method.fixed b/tests/ui/regions/regions-escape-method.fixed new file mode 100644 index 000000000000..f192dca1e25f --- /dev/null +++ b/tests/ui/regions/regions-escape-method.fixed @@ -0,0 +1,17 @@ +// Test a method call where the parameter `B` would (illegally) be +// inferred to a region bound in the method argument. If this program +// were accepted, then the closure passed to `s.f` could escape its +// argument. +//@ run-rustfix + +struct S; + +impl S { + fn f(&self, _: F) where F: FnOnce(&i32) -> B { + } +} + +fn main() { + let s = S; + s.f(|p| *p) //~ ERROR lifetime may not live long enough +} diff --git a/tests/ui/regions/regions-escape-method.rs b/tests/ui/regions/regions-escape-method.rs index 69c01ae6906c..82bf86c79b23 100644 --- a/tests/ui/regions/regions-escape-method.rs +++ b/tests/ui/regions/regions-escape-method.rs @@ -2,6 +2,7 @@ // inferred to a region bound in the method argument. If this program // were accepted, then the closure passed to `s.f` could escape its // argument. +//@ run-rustfix struct S; diff --git a/tests/ui/regions/regions-escape-method.stderr b/tests/ui/regions/regions-escape-method.stderr index aeda923b0ba9..687b91bb7b47 100644 --- a/tests/ui/regions/regions-escape-method.stderr +++ b/tests/ui/regions/regions-escape-method.stderr @@ -1,11 +1,16 @@ error: lifetime may not live long enough - --> $DIR/regions-escape-method.rs:15:13 + --> $DIR/regions-escape-method.rs:16:13 | LL | s.f(|p| p) | -- ^ returning this value requires that `'1` must outlive `'2` | || | |return type of closure is &'2 i32 | has type `&'1 i32` + | +help: dereference the return value + | +LL | s.f(|p| *p) + | + error: aborting due to 1 previous error From 6521c3971d0818ab37f27f36f2eb99de420c780f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 20 Jun 2024 12:17:42 -0400 Subject: [PATCH 08/11] Deny use<> for RPITITs --- compiler/rustc_ast_lowering/messages.ftl | 3 ++ compiler/rustc_ast_lowering/src/errors.rs | 8 +++++ compiler/rustc_ast_lowering/src/lib.rs | 20 +++++++++++ .../forgot-to-capture-type.rs | 1 + .../forgot-to-capture-type.stderr | 10 +++++- .../precise-capturing/redundant.normal.stderr | 20 +++++++++++ .../precise-capturing/redundant.rpitit.stderr | 18 ++++++++++ .../impl-trait/precise-capturing/redundant.rs | 13 ++++--- .../precise-capturing/redundant.stderr | 36 ------------------- .../precise-capturing/rpitit.stderr | 10 +++++- .../precise-capturing/self-capture.rs | 3 +- .../precise-capturing/self-capture.stderr | 10 ++++++ 12 files changed, 107 insertions(+), 45 deletions(-) create mode 100644 tests/ui/impl-trait/precise-capturing/redundant.normal.stderr create mode 100644 tests/ui/impl-trait/precise-capturing/redundant.rpitit.stderr delete mode 100644 tests/ui/impl-trait/precise-capturing/redundant.stderr create mode 100644 tests/ui/impl-trait/precise-capturing/self-capture.stderr diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index 7d81e45d314d..57b9d904dc2f 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -130,6 +130,9 @@ ast_lowering_never_pattern_with_guard = ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed in argument-position `impl Trait` +ast_lowering_no_precise_captures_on_rpitit = `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits + .note = currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope + ast_lowering_previously_used_here = previously used here ast_lowering_register1 = register `{$reg1_name}` diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 02744d16b422..3d4b6a1f033f 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -424,6 +424,14 @@ pub(crate) struct NoPreciseCapturesOnApit { pub span: Span, } +#[derive(Diagnostic)] +#[diag(ast_lowering_no_precise_captures_on_rpitit)] +#[note] +pub(crate) struct NoPreciseCapturesOnRpitit { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(ast_lowering_yield_in_closure)] pub(crate) struct YieldInClosure { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index da8682d3d095..0a06304fcecf 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1594,6 +1594,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; debug!(?captured_lifetimes_to_duplicate); + match fn_kind { + // Deny `use<>` on RPITIT in trait/trait-impl for now. + Some(FnDeclKind::Trait | FnDeclKind::Impl) => { + if let Some(span) = bounds.iter().find_map(|bound| match *bound { + ast::GenericBound::Use(_, span) => Some(span), + _ => None, + }) { + self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnRpitit { span }); + } + } + None + | Some( + FnDeclKind::Fn + | FnDeclKind::Inherent + | FnDeclKind::ExternFn + | FnDeclKind::Closure + | FnDeclKind::Pointer, + ) => {} + } + self.lower_opaque_inner( opaque_ty_node_id, origin, diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs index 080149857839..0028a45cbf3f 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs @@ -6,6 +6,7 @@ fn type_param() -> impl Sized + use<> {} trait Foo { fn bar() -> impl Sized + use<>; //~^ ERROR `impl Trait` must mention the `Self` type of the trait + //~| ERROR `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits } fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr index 93b44a0c18c2..89bd4df44310 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr @@ -1,3 +1,11 @@ +error: `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits + --> $DIR/forgot-to-capture-type.rs:7:30 + | +LL | fn bar() -> impl Sized + use<>; + | ^^^^^ + | + = note: currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope + error: `impl Trait` must mention all type parameters in scope in `use<...>` --> $DIR/forgot-to-capture-type.rs:3:23 | @@ -18,5 +26,5 @@ LL | fn bar() -> impl Sized + use<>; | = note: currently, all type parameters are required to be mentioned in the precise captures list -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/impl-trait/precise-capturing/redundant.normal.stderr b/tests/ui/impl-trait/precise-capturing/redundant.normal.stderr new file mode 100644 index 000000000000..44bc9f7daad8 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/redundant.normal.stderr @@ -0,0 +1,20 @@ +warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant + --> $DIR/redundant.rs:7:19 + | +LL | fn hello<'a>() -> impl Sized + use<'a> {} + | ^^^^^^^^^^^^^------- + | | + | help: remove the `use<...>` syntax + | + = note: `#[warn(impl_trait_redundant_captures)]` on by default + +warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant + --> $DIR/redundant.rs:12:27 + | +LL | fn inherent(&self) -> impl Sized + use<'_> {} + | ^^^^^^^^^^^^^------- + | | + | help: remove the `use<...>` syntax + +warning: 2 warnings emitted + diff --git a/tests/ui/impl-trait/precise-capturing/redundant.rpitit.stderr b/tests/ui/impl-trait/precise-capturing/redundant.rpitit.stderr new file mode 100644 index 000000000000..9aa73353126c --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/redundant.rpitit.stderr @@ -0,0 +1,18 @@ +error: `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits + --> $DIR/redundant.rs:18:35 + | +LL | fn in_trait() -> impl Sized + use<'a, Self>; + | ^^^^^^^^^^^^^ + | + = note: currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope + +error: `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits + --> $DIR/redundant.rs:23:35 + | +LL | fn in_trait() -> impl Sized + use<'a> {} + | ^^^^^^^ + | + = note: currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope + +error: aborting due to 2 previous errors + diff --git a/tests/ui/impl-trait/precise-capturing/redundant.rs b/tests/ui/impl-trait/precise-capturing/redundant.rs index 99c128fdc482..ef4f05bd7e45 100644 --- a/tests/ui/impl-trait/precise-capturing/redundant.rs +++ b/tests/ui/impl-trait/precise-capturing/redundant.rs @@ -1,24 +1,27 @@ //@ compile-flags: -Zunstable-options --edition=2024 -//@ check-pass +//@ revisions: normal rpitit +//@[normal] check-pass #![feature(precise_capturing)] fn hello<'a>() -> impl Sized + use<'a> {} -//~^ WARN all possible in-scope parameters are already captured +//[normal]~^ WARN all possible in-scope parameters are already captured struct Inherent; impl Inherent { fn inherent(&self) -> impl Sized + use<'_> {} - //~^ WARN all possible in-scope parameters are already captured + //[normal]~^ WARN all possible in-scope parameters are already captured } +#[cfg(rpitit)] trait Test<'a> { fn in_trait() -> impl Sized + use<'a, Self>; - //~^ WARN all possible in-scope parameters are already captured + //[rpitit]~^ ERROR `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits } +#[cfg(rpitit)] impl<'a> Test<'a> for () { fn in_trait() -> impl Sized + use<'a> {} - //~^ WARN all possible in-scope parameters are already captured + //[rpitit]~^ ERROR `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits } fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/redundant.stderr b/tests/ui/impl-trait/precise-capturing/redundant.stderr deleted file mode 100644 index 274d9d2375f7..000000000000 --- a/tests/ui/impl-trait/precise-capturing/redundant.stderr +++ /dev/null @@ -1,36 +0,0 @@ -warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant - --> $DIR/redundant.rs:6:19 - | -LL | fn hello<'a>() -> impl Sized + use<'a> {} - | ^^^^^^^^^^^^^------- - | | - | help: remove the `use<...>` syntax - | - = note: `#[warn(impl_trait_redundant_captures)]` on by default - -warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant - --> $DIR/redundant.rs:11:27 - | -LL | fn inherent(&self) -> impl Sized + use<'_> {} - | ^^^^^^^^^^^^^------- - | | - | help: remove the `use<...>` syntax - -warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant - --> $DIR/redundant.rs:16:22 - | -LL | fn in_trait() -> impl Sized + use<'a, Self>; - | ^^^^^^^^^^^^^------------- - | | - | help: remove the `use<...>` syntax - -warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant - --> $DIR/redundant.rs:20:22 - | -LL | fn in_trait() -> impl Sized + use<'a> {} - | ^^^^^^^^^^^^^------- - | | - | help: remove the `use<...>` syntax - -warning: 4 warnings emitted - diff --git a/tests/ui/impl-trait/precise-capturing/rpitit.stderr b/tests/ui/impl-trait/precise-capturing/rpitit.stderr index 202eeb39385d..45eceef2f494 100644 --- a/tests/ui/impl-trait/precise-capturing/rpitit.stderr +++ b/tests/ui/impl-trait/precise-capturing/rpitit.stderr @@ -1,3 +1,11 @@ +error: `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits + --> $DIR/rpitit.rs:11:36 + | +LL | fn hello() -> impl PartialEq + use; + | ^^^^^^^^^ + | + = note: currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope + error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list --> $DIR/rpitit.rs:11:19 | @@ -38,5 +46,5 @@ LL | | ); help: `'a` and `'b` must be the same: replace one with the other -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/impl-trait/precise-capturing/self-capture.rs b/tests/ui/impl-trait/precise-capturing/self-capture.rs index e0a4a8b658c5..07bb417f9f7f 100644 --- a/tests/ui/impl-trait/precise-capturing/self-capture.rs +++ b/tests/ui/impl-trait/precise-capturing/self-capture.rs @@ -1,9 +1,8 @@ -//@ check-pass - #![feature(precise_capturing)] trait Foo { fn bar<'a>() -> impl Sized + use; + //~^ ERROR `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits } fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/self-capture.stderr b/tests/ui/impl-trait/precise-capturing/self-capture.stderr new file mode 100644 index 000000000000..351de86dd5fa --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/self-capture.stderr @@ -0,0 +1,10 @@ +error: `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits + --> $DIR/self-capture.rs:4:34 + | +LL | fn bar<'a>() -> impl Sized + use; + | ^^^^^^^^^ + | + = note: currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope + +error: aborting due to 1 previous error + From 553a69030e5a086eb3841d020db8c9c463948c72 Mon Sep 17 00:00:00 2001 From: Ana Hobden Date: Mon, 24 Jun 2024 12:57:14 -0700 Subject: [PATCH 09/11] Specify target specific linker for riscv64gc-gnu job --- src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile b/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile index a9ffa5918b5b..a52c3839196f 100644 --- a/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile @@ -91,7 +91,9 @@ RUN sh /scripts/sccache.sh # Avoid "fatal: detected dubious ownership in repository at '/checkout'" error RUN git config --global --add safe.directory /checkout -ENV RUST_CONFIGURE_ARGS --qemu-riscv64-rootfs=/tmp/rootfs +ENV RUST_CONFIGURE_ARGS \ + --qemu-riscv64-rootfs=/tmp/rootfs \ + --set target.riscv64gc-unknown-linux-gnu.linker=riscv64-linux-gnu-gcc ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target riscv64gc-unknown-linux-gnu ENV NO_CHANGE_USER=1 From 26677eb06efbd976e2a3b1c30eacf8e22c8c831c Mon Sep 17 00:00:00 2001 From: SparkyPotato Date: Mon, 24 Jun 2024 16:20:22 -0500 Subject: [PATCH 10/11] don't suggest awaiting type expr patterns --- .../src/infer/error_reporting/suggest.rs | 6 ++++-- tests/ui/async-await/suggest-missing-await.rs | 7 +++++++ tests/ui/async-await/suggest-missing-await.stderr | 14 +++++++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 74c65e93616e..13b145296a7c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -209,8 +209,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code() { - ObligationCauseCode::Pattern { span: Some(then_span), .. } => { - Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() }) + ObligationCauseCode::Pattern { span: Some(then_span), origin_expr, .. } => { + origin_expr.then_some(ConsiderAddingAwait::FutureSugg { + span: then_span.shrink_to_hi(), + }) } ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => { let then_span = self.find_block_span_from_hir_id(*then_id); diff --git a/tests/ui/async-await/suggest-missing-await.rs b/tests/ui/async-await/suggest-missing-await.rs index 96996af0bd2d..0bd67cec335b 100644 --- a/tests/ui/async-await/suggest-missing-await.rs +++ b/tests/ui/async-await/suggest-missing-await.rs @@ -71,4 +71,11 @@ async fn suggest_await_in_generic_pattern() { } } +// Issue #126903 +async fn do_async() {} +fn dont_suggest_awaiting_closure_patterns() { + Some(do_async()).map(|()| {}); + //~^ ERROR mismatched types [E0308] +} + fn main() {} diff --git a/tests/ui/async-await/suggest-missing-await.stderr b/tests/ui/async-await/suggest-missing-await.stderr index f0ec34a6a555..f9db86ea40a9 100644 --- a/tests/ui/async-await/suggest-missing-await.stderr +++ b/tests/ui/async-await/suggest-missing-await.stderr @@ -133,6 +133,18 @@ help: consider `await`ing on the `Future` LL | match dummy_result().await { | ++++++ -error: aborting due to 7 previous errors +error[E0308]: mismatched types + --> $DIR/suggest-missing-await.rs:77:27 + | +LL | Some(do_async()).map(|()| {}); + | ^^ + | | + | expected future, found `()` + | expected due to this + | + = note: expected opaque type `impl Future` + found unit type `()` + +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0308`. From 8016940ef41b1245aecf9eb19c02b85f45dcd133 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 24 Jun 2024 12:50:04 +1000 Subject: [PATCH 11/11] Tweak a confusing comment in `create_match_candidates` --- compiler/rustc_mir_build/src/build/matches/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 68244136d1ad..350b00db7fdd 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -358,8 +358,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { where 'a: 'pat, { - // Assemble a list of candidates: there is one candidate per pattern, - // which means there may be more than one candidate *per arm*. + // Assemble the initial list of candidates. These top-level candidates + // are 1:1 with the original match arms, but other parts of match + // lowering also introduce subcandidates (for subpatterns), and will + // also flatten candidates in some cases. So in general a list of + // candidates does _not_ necessarily correspond to a list of arms. arms.iter() .copied() .map(|arm| {