From 2e2cd549242fd53a1529340fdcaf68759811af74 Mon Sep 17 00:00:00 2001 From: ozankenangungor Date: Mon, 7 Sep 2026 02:23:55 +0300 Subject: [PATCH 01/17] Improve diagnostics for non-well-formed associated items --- .../traits/fulfillment_errors.rs | 33 ++++- .../src/error_reporting/traits/suggestions.rs | 59 +++++++++ .../src/solve/fulfill/derive_errors.rs | 39 ++++-- ...item-own-bound-issue-162368.current.stderr | 17 +++ ...oc-item-own-bound-issue-162368.next.stderr | 27 ++++ .../assoc-item-own-bound-issue-162368.rs | 17 +++ ...-item-own-bound-where-clause-suggestion.rs | 67 ++++++++++ ...m-own-bound-where-clause-suggestion.stderr | 119 ++++++++++++++++++ ...rivial-unsized-projection-2.bad_new.stderr | 34 +++-- .../ui/traits/trivial-unsized-projection-2.rs | 3 +- .../trivial-unsized-projection.bad_new.stderr | 29 +++-- tests/ui/traits/trivial-unsized-projection.rs | 3 +- 12 files changed, 410 insertions(+), 37 deletions(-) create mode 100644 tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.current.stderr create mode 100644 tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.next.stderr create mode 100644 tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.rs create mode 100644 tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs create mode 100644 tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 47d1f1b22c090..10ea471f18fc1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -175,6 +175,31 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Err(guar) = leaf_trait_predicate.error_reported() { return guar; } + let impl_assoc_type_self_trait_bound_param = + if self.is_explicit_self_trait_bound_on_impl_assoc_type( + obligation.cause.body_def_id, + obligation.cause.code(), + leaf_trait_predicate.def_id(), + ) && let ty::Param(param_ty) = + *leaf_trait_predicate.skip_binder().self_ty().kind() + { + Some(param_ty) + } else { + None + }; + + let is_explicit_self_sized_bound_on_impl_assoc_type = + impl_assoc_type_self_trait_bound_param.is_some() + && self + .tcx + .is_lang_item(leaf_trait_predicate.def_id(), LangItem::Sized); + + if let Some(param_ty) = impl_assoc_type_self_trait_bound_param { + span = param_ty.span_from_generics( + self.tcx, + obligation.cause.body_def_id.to_def_id(), + ); + } // Silence redundant errors on binding access that are already // reported on the binding definition (#56607). if let Err(guar) = self.fn_arg_obligation(&obligation) { @@ -393,9 +418,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => DUMMY_SP, }; if let Some(s) = label { - // If it has a custom `#[rustc_on_unimplemented]` - // error message, let's display it as the label! - err.span_label(span, s); + // Preserve custom `#[rustc_on_unimplemented]` labels for other traits; + // the explicit `Self: Sized` associated-type case has targeted labeling. + if !is_explicit_self_sized_bound_on_impl_assoc_type { + err.span_label(span, s); + } if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point 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 5a17d65b0c0d6..f7f0a137ef18b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3834,6 +3834,38 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); true } + /// Checks for an explicit `Self: Trait` bound on an associated type impl. + pub(super) fn is_explicit_self_trait_bound_on_impl_assoc_type( + &self, + body_def_id: LocalDefId, + cause_code: &ObligationCauseCode<'tcx>, + trait_def_id: DefId, + ) -> bool { + let ObligationCauseCode::WhereClause(item_def_id, span) = *cause_code.peel_derives() else { + return false; + }; + let is_impl_trait_assoc_type = if let Some(assoc_item) = + self.tcx.opt_associated_item(body_def_id.to_def_id()) + && let Some(trait_item_def_id) = assoc_item.trait_item_def_id() + { + trait_item_def_id == item_def_id + && matches!(assoc_item.kind, ty::AssocKind::Type { .. }) + } else { + false + }; + + if !is_impl_trait_assoc_type { + return false; + } + + self.tcx.clauses_of(item_def_id).instantiate_own_identity().any(|(clause, clause_span)| { + clause_span == span + && clause.skip_norm_wip().as_trait_clause().is_some_and(|trait_pred| { + trait_pred.def_id() == trait_def_id + && trait_pred.skip_binder().self_ty().is_self_param() + }) + }) + } pub(crate) fn note_obligation_cause_code( &self, @@ -4022,6 +4054,32 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { this = "the implicit `Sized` requirement on this type parameter"; } + let is_explicit_self_sized_bound_on_impl_assoc_type = self + .is_explicit_self_trait_bound_on_impl_assoc_type( + body_def_id, + cause_code, + def_id, + ); + + if is_explicit_self_sized_bound_on_impl_assoc_type + && let Some(generics) = tcx.hir_get_generics(body_def_id) + && !generics.where_clause_span.from_expansion() + && generics.where_clause_span.desugaring_kind().is_none() + { + let tail_span = generics.tail_span_for_predicate_suggestion(); + if tail_span.can_be_used_for_suggestions() { + err.span_suggestion_verbose( + tail_span, + "consider adding `where Self: Sized`", + format!( + "{} Self: Sized", + generics.add_where_or_trailing_comma() + ), + Applicability::MachineApplicable, + ); + } + } + if let Some(hir::Node::TraitItem(hir::TraitItem { generics, kind: hir::TraitItemKind::Type(bounds, None), @@ -4031,6 +4089,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && !bounds.iter() .filter_map(|bound| bound.trait_ref()) .any(|tr| tr.trait_def_id().is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized))) + && !is_explicit_self_sized_bound_on_impl_assoc_type { let (span, separator) = if let [.., last] = bounds { (last.span().shrink_to_hi(), " +") diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index 992c521261b5c..634a17dc372ef 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -350,19 +350,42 @@ impl<'tcx> BestObligation<'tcx> { alias: ty::AliasTerm<'tcx>, ) -> ControlFlow> { let tcx = goal.infcx().tcx; - let obligation = Obligation::new( - tcx, - self.obligation.cause.clone(), - goal.goal().param_env, - alias.trait_ref(tcx), - ); + + let trait_ref = alias.trait_ref(tcx); + let obligation = + Obligation::new(tcx, self.obligation.cause.clone(), goal.goal().param_env, trait_ref); + self.with_derived_obligation(obligation, |this| { goal.infcx().visit_proof_tree_at_depth( - goal.goal().with(tcx, alias.trait_ref(tcx)), + goal.goal().with(tcx, trait_ref), goal.depth() + 1, this, ) - }) + })?; + + let def_id = alias.expect_projection_def_id(); + + for (clause, span) in tcx.clauses_of(def_id).instantiate_own(tcx, alias.args) { + let clause = clause.skip_norm_wip(); + + let cause = ObligationCause::new( + self.obligation.cause.span, + self.obligation.cause.body_def_id, + ObligationCauseCode::WhereClause(def_id, span), + ); + + let obligation = Obligation::new(tcx, cause, goal.goal().param_env, clause); + + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(tcx, clause), + goal.depth() + 1, + this, + ) + })?; + } + + ControlFlow::Continue(()) } /// If we have no candidates, then it's likely that there is a diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.current.stderr b/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.current.stderr new file mode 100644 index 0000000000000..00378f33a5597 --- /dev/null +++ b/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.current.stderr @@ -0,0 +1,17 @@ +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/assoc-item-own-bound-issue-162368.rs:13:18 + | +LL | impl Trait for T { + | - this type parameter needs to be `Sized` +LL | type Assoc = i32; + | ^^^ doesn't have a size known at compile-time + | +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - impl Trait for T { +LL + impl Trait for T { + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.next.stderr b/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.next.stderr new file mode 100644 index 0000000000000..c2a9e40177178 --- /dev/null +++ b/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.next.stderr @@ -0,0 +1,27 @@ +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/assoc-item-own-bound-issue-162368.rs:12:6 + | +LL | impl Trait for T { + | ^ this type parameter needs to be `Sized` + | +note: required by a bound in `Trait::Assoc` + --> $DIR/assoc-item-own-bound-issue-162368.rs:8:15 + | +LL | type Assoc + | ----- required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `Trait::Assoc` +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - impl Trait for T { +LL + impl Trait for T { + | +help: consider adding `where Self: Sized` + | +LL | type Assoc = i32 where Self: Sized; + | +++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.rs b/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.rs new file mode 100644 index 0000000000000..b6356c9b72d76 --- /dev/null +++ b/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.rs @@ -0,0 +1,17 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +trait Trait { + type Assoc + where + Self: Sized; +} + +//[next]~v ERROR the size for values of type `T` cannot be known at compilation time +impl Trait for T { + type Assoc = i32; + //[current]~^ ERROR the size for values of type `T` cannot be known at compilation time +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs b/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs new file mode 100644 index 0000000000000..f9553fbee821d --- /dev/null +++ b/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs @@ -0,0 +1,67 @@ +//@ compile-flags: -Znext-solver + +// Case B: implementation associated type already has an existing where-clause +trait TraitWithBound { + type Assoc + where + Self: Sized, + Self: 'static; +} + +impl TraitWithBound for T { + //~^ ERROR the size for values of type `T` cannot be known at compilation time + type Assoc = i32 + where + Self: 'static; +} + +// Case C: normal associated-type implicit-Sized diagnostic (suppression is not global) +trait NormalAssoc { + type Assoc; +} + +impl NormalAssoc for () { + type Assoc = [u8]; + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time +} + +// Case D: projection/non-impl use-site (targeted suggestion does not leak) +trait NonImplTrait { + type Assoc + where + Self: Sized; +} + +fn non_impl_use() { + let _: ::Assoc; + //~^ ERROR the size for values of type `T` cannot be known at compilation time + //~| ERROR the trait bound `T: NonImplTrait` is not satisfied +} + +// Case E: explicit associated-type bound with a trait other than `Sized` +trait Marker {} + +trait TraitWithMarker { + type Assoc + where + Self: Marker; +} + +impl TraitWithMarker for T { + //~^ ERROR the trait bound `T: Marker` is not satisfied + type Assoc = i32; +} + +// Case F: associated-type bound with a custom on-unimplemented diagnostic +trait TraitWithSend { + type Assoc + where + Self: Send; +} + +impl TraitWithSend for T { + //~^ ERROR `T` cannot be sent between threads safely + type Assoc = i32; +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.stderr b/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.stderr new file mode 100644 index 0000000000000..dcca095ce7717 --- /dev/null +++ b/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.stderr @@ -0,0 +1,119 @@ +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:11:6 + | +LL | impl TraitWithBound for T { + | ^ this type parameter needs to be `Sized` + | +note: required by a bound in `TraitWithBound::Assoc` + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:7:15 + | +LL | type Assoc + | ----- required by a bound in this associated type +LL | where +LL | Self: Sized, + | ^^^^^ required by this bound in `TraitWithBound::Assoc` +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - impl TraitWithBound for T { +LL + impl TraitWithBound for T { + | +help: consider adding `where Self: Sized` + | +LL | Self: 'static, Self: Sized; + | +++++++++++++ + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:24:18 + | +LL | type Assoc = [u8]; + | ^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `NormalAssoc::Assoc` + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:20:5 + | +LL | type Assoc; + | ^^^^^^^^^^^ required by this bound in `NormalAssoc::Assoc` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assoc: ?Sized; + | ++++++++ + +error[E0277]: the trait bound `T: Marker` is not satisfied + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:50:6 + | +LL | impl TraitWithMarker for T { + | ^ the trait `Marker` is not implemented for `T` + | +note: required by a bound in `TraitWithMarker::Assoc` + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:47:15 + | +LL | type Assoc + | ----- required by a bound in this associated type +LL | where +LL | Self: Marker; + | ^^^^^^ required by this bound in `TraitWithMarker::Assoc` +help: consider restricting type parameter `T` with trait `Marker` + | +LL | impl TraitWithMarker for T { + | ++++++++ + +error[E0277]: `T` cannot be sent between threads safely + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:62:6 + | +LL | impl TraitWithSend for T { + | ^ `T` cannot be sent between threads safely + | +note: required by a bound in `TraitWithSend::Assoc` + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:59:15 + | +LL | type Assoc + | ----- required by a bound in this associated type +LL | where +LL | Self: Send; + | ^^^^ required by this bound in `TraitWithSend::Assoc` +help: consider restricting type parameter `T` with trait `Send` + | +LL | impl TraitWithSend for T { + | +++++++++++++++++++ + +error[E0277]: the trait bound `T: NonImplTrait` is not satisfied + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:36:13 + | +LL | let _: ::Assoc; + | ^ the trait `NonImplTrait` is not implemented for `T` + | +help: consider further restricting type parameter `T` with trait `NonImplTrait` + | +LL | fn non_impl_use() { + | ++++++++++++++ + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:36:13 + | +LL | fn non_impl_use() { + | - this type parameter needs to be `Sized` +LL | let _: ::Assoc; + | ^ doesn't have a size known at compile-time + | +note: required by a bound in `NonImplTrait::Assoc` + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:32:15 + | +LL | type Assoc + | ----- required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `NonImplTrait::Assoc` +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - fn non_impl_use() { +LL + fn non_impl_use() { + | +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assoc: ?Sized + | ++++++++ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr b/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr index a58cebabf75d8..b378a7406ecf5 100644 --- a/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr +++ b/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr @@ -1,9 +1,3 @@ -error[E0271]: type mismatch resolving `::Assert == _` - --> $DIR/trivial-unsized-projection-2.rs:22:12 - | -LL | const FOO: ::Assert = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^ types differ - error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection-2.rs:22:12 | @@ -29,13 +23,31 @@ help: consider relaxing the implicit `Sized` restriction LL | type Assert: ?Sized | ++++++++ -error[E0271]: type mismatch resolving `::Assert == _` +error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection-2.rs:22:36 | LL | const FOO: ::Assert = todo!(); - | ^^^^^^^ types differ + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: within `Tail`, the trait `Sized` is not implemented for `[()]` +note: required because it appears within the type `Tail` + --> $DIR/trivial-unsized-projection-2.rs:17:8 + | +LL | struct Tail([()]); + | ^^^^ +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection-2.rs:14:15 + | +LL | type Assert + | ------ required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `Bad::Assert` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assert: ?Sized + | ++++++++ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0271, E0277. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trivial-unsized-projection-2.rs b/tests/ui/traits/trivial-unsized-projection-2.rs index 175a2712d25b5..af4e12f6f9008 100644 --- a/tests/ui/traits/trivial-unsized-projection-2.rs +++ b/tests/ui/traits/trivial-unsized-projection-2.rs @@ -23,8 +23,7 @@ const FOO: ::Assert = todo!(); //[bad]~^ ERROR the size for values of type `[()]` cannot be known at compilation time //[bad]~| ERROR the size for values of type `[()]` cannot be known at compilation time //[bad_new]~^^^ ERROR the size for values of type `[()]` cannot be known at compilation time -//[bad_new]~| ERROR: type mismatch resolving `::Assert == _` -//[bad_new]~| ERROR: type mismatch resolving `::Assert == _` +//[bad_new]~| ERROR the size for values of type `[()]` cannot be known at compilation time #[cfg(any(good, good_new))] // Well-formed in trivially false param-env diff --git a/tests/ui/traits/trivial-unsized-projection.bad_new.stderr b/tests/ui/traits/trivial-unsized-projection.bad_new.stderr index a055181542d4d..d5d829285305a 100644 --- a/tests/ui/traits/trivial-unsized-projection.bad_new.stderr +++ b/tests/ui/traits/trivial-unsized-projection.bad_new.stderr @@ -1,9 +1,3 @@ -error[E0271]: type mismatch resolving `<[()] as Bad>::Assert == _` - --> $DIR/trivial-unsized-projection.rs:20:12 - | -LL | const FOO: <[()] as Bad>::Assert = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^ types differ - error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection.rs:20:12 | @@ -24,13 +18,26 @@ help: consider relaxing the implicit `Sized` restriction LL | type Assert: ?Sized | ++++++++ -error[E0271]: type mismatch resolving `<[()] as Bad>::Assert == _` +error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection.rs:20:36 | LL | const FOO: <[()] as Bad>::Assert = todo!(); - | ^^^^^^^ types differ + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[()]` +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection.rs:14:15 + | +LL | type Assert + | ------ required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `Bad::Assert` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assert: ?Sized + | ++++++++ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0271, E0277. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trivial-unsized-projection.rs b/tests/ui/traits/trivial-unsized-projection.rs index 8b62356785a35..62ff25fb7ac0a 100644 --- a/tests/ui/traits/trivial-unsized-projection.rs +++ b/tests/ui/traits/trivial-unsized-projection.rs @@ -21,8 +21,7 @@ const FOO: <[()] as Bad>::Assert = todo!(); //[bad]~^ ERROR the size for values of type `[()]` cannot be known at compilation time //[bad]~| ERROR the size for values of type `[()]` cannot be known at compilation time //[bad_new]~^^^ ERROR the size for values of type `[()]` cannot be known at compilation time -//[bad_new]~| ERROR type mismatch resolving `<[()] as Bad>::Assert == _` -//[bad_new]~| ERROR type mismatch resolving `<[()] as Bad>::Assert == _` +//[bad_new]~| ERROR the size for values of type `[()]` cannot be known at compilation time #[cfg(any(good, good_new))] // Well-formed in trivially false param-env From 29d1ae1dc6f260517cfbed020aff70afb315e9df Mon Sep 17 00:00:00 2001 From: ozankenangungor Date: Sat, 10 Oct 2026 07:23:35 +0300 Subject: [PATCH 02/17] Handle explicit associated item own bounds generally Remove the `where Self: Sized` suggestion and the Sized-specific label suppression so an explicit `Self: Sized` clause is reported like any other `Self: Trait` bound, and only suggest relaxing the implicit `Sized` restriction when the failing obligation is that implicit restriction rather than an explicit where clause of the item. Also rename the regression tests and add a case for an implicit `Sized` failure on an associated item with other explicit where clauses. --- .../traits/fulfillment_errors.rs | 47 +++++++--- .../src/error_reporting/traits/suggestions.rs | 66 ++----------- .../assoc_type_bounds_sized_used.stderr | 4 - .../assoc-item-own-bound-diagnostics.rs | 92 +++++++++++++++++++ ...> assoc-item-own-bound-diagnostics.stderr} | 64 ++++++++----- ...-item-own-bound-where-clause-suggestion.rs | 67 -------------- ...rr => assoc-item-own-bound.current.stderr} | 2 +- ...tderr => assoc-item-own-bound.next.stderr} | 15 ++- ...ssue-162368.rs => assoc-item-own-bound.rs} | 5 + .../trivial-unsized-projection-2.bad.stderr | 8 -- ...rivial-unsized-projection-2.bad_new.stderr | 14 +-- .../trivial-unsized-projection.bad.stderr | 8 -- .../trivial-unsized-projection.bad_new.stderr | 10 +- 13 files changed, 192 insertions(+), 210 deletions(-) create mode 100644 tests/ui/traits/next-solver/assoc-item-own-bound-diagnostics.rs rename tests/ui/traits/next-solver/{assoc-item-own-bound-where-clause-suggestion.stderr => assoc-item-own-bound-diagnostics.stderr} (68%) delete mode 100644 tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs rename tests/ui/traits/next-solver/{assoc-item-own-bound-issue-162368.current.stderr => assoc-item-own-bound.current.stderr} (90%) rename tests/ui/traits/next-solver/{assoc-item-own-bound-issue-162368.next.stderr => assoc-item-own-bound.next.stderr} (61%) rename tests/ui/traits/next-solver/{assoc-item-own-bound-issue-162368.rs => assoc-item-own-bound.rs} (61%) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 10ea471f18fc1..000a4c1cdec43 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -57,6 +57,39 @@ use crate::traits::{ }; impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + /// Checks for an explicit `Self: Trait` bound on an associated type impl. + fn is_explicit_self_trait_bound_on_impl_assoc_type( + &self, + body_def_id: LocalDefId, + cause_code: &ObligationCauseCode<'tcx>, + trait_def_id: DefId, + ) -> bool { + let ObligationCauseCode::WhereClause(item_def_id, span) = *cause_code.peel_derives() else { + return false; + }; + let is_impl_trait_assoc_type = if let Some(assoc_item) = + self.tcx.opt_associated_item(body_def_id.to_def_id()) + && let Some(trait_item_def_id) = assoc_item.trait_item_def_id() + { + trait_item_def_id == item_def_id + && matches!(assoc_item.kind, ty::AssocKind::Type { .. }) + } else { + false + }; + + if !is_impl_trait_assoc_type { + return false; + } + + self.tcx.clauses_of(item_def_id).instantiate_own_identity().any(|(clause, clause_span)| { + clause_span == span + && clause.skip_norm_wip().as_trait_clause().is_some_and(|trait_pred| { + trait_pred.def_id() == trait_def_id + && trait_pred.skip_binder().self_ty().is_self_param() + }) + }) + } + /// The `root_obligation` parameter should be the `root_obligation` field /// from a `FulfillmentError`. If no `FulfillmentError` is available, /// then it should be the same as `obligation`. @@ -188,12 +221,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { None }; - let is_explicit_self_sized_bound_on_impl_assoc_type = - impl_assoc_type_self_trait_bound_param.is_some() - && self - .tcx - .is_lang_item(leaf_trait_predicate.def_id(), LangItem::Sized); - if let Some(param_ty) = impl_assoc_type_self_trait_bound_param { span = param_ty.span_from_generics( self.tcx, @@ -418,11 +445,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => DUMMY_SP, }; if let Some(s) = label { - // Preserve custom `#[rustc_on_unimplemented]` labels for other traits; - // the explicit `Self: Sized` associated-type case has targeted labeling. - if !is_explicit_self_sized_bound_on_impl_assoc_type { - err.span_label(span, s); - } + // If it has a custom `#[rustc_on_unimplemented]` + // error message, let's display it as the label! + err.span_label(span, s); if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point 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 f7f0a137ef18b..33db9f71522a7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3834,38 +3834,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); true } - /// Checks for an explicit `Self: Trait` bound on an associated type impl. - pub(super) fn is_explicit_self_trait_bound_on_impl_assoc_type( - &self, - body_def_id: LocalDefId, - cause_code: &ObligationCauseCode<'tcx>, - trait_def_id: DefId, - ) -> bool { - let ObligationCauseCode::WhereClause(item_def_id, span) = *cause_code.peel_derives() else { - return false; - }; - let is_impl_trait_assoc_type = if let Some(assoc_item) = - self.tcx.opt_associated_item(body_def_id.to_def_id()) - && let Some(trait_item_def_id) = assoc_item.trait_item_def_id() - { - trait_item_def_id == item_def_id - && matches!(assoc_item.kind, ty::AssocKind::Type { .. }) - } else { - false - }; - - if !is_impl_trait_assoc_type { - return false; - } - - self.tcx.clauses_of(item_def_id).instantiate_own_identity().any(|(clause, clause_span)| { - clause_span == span - && clause.skip_norm_wip().as_trait_clause().is_some_and(|trait_pred| { - trait_pred.def_id() == trait_def_id - && trait_pred.skip_binder().self_ty().is_self_param() - }) - }) - } pub(crate) fn note_obligation_cause_code( &self, @@ -4054,32 +4022,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { this = "the implicit `Sized` requirement on this type parameter"; } - let is_explicit_self_sized_bound_on_impl_assoc_type = self - .is_explicit_self_trait_bound_on_impl_assoc_type( - body_def_id, - cause_code, - def_id, - ); - - if is_explicit_self_sized_bound_on_impl_assoc_type - && let Some(generics) = tcx.hir_get_generics(body_def_id) - && !generics.where_clause_span.from_expansion() - && generics.where_clause_span.desugaring_kind().is_none() - { - let tail_span = generics.tail_span_for_predicate_suggestion(); - if tail_span.can_be_used_for_suggestions() { - err.span_suggestion_verbose( - tail_span, - "consider adding `where Self: Sized`", - format!( - "{} Self: Sized", - generics.add_where_or_trailing_comma() - ), - Applicability::MachineApplicable, - ); - } - } - if let Some(hir::Node::TraitItem(hir::TraitItem { generics, kind: hir::TraitItemKind::Type(bounds, None), @@ -4089,7 +4031,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && !bounds.iter() .filter_map(|bound| bound.trait_ref()) .any(|tr| tr.trait_def_id().is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized))) - && !is_explicit_self_sized_bound_on_impl_assoc_type + // Only suggest relaxing the implicit `Sized` restriction if the + // failing obligation is that restriction. Relaxing it cannot + // satisfy an explicit where clause of the item, e.g. + // `type Assoc where Self: Sized;`. + && !generics.predicates.iter().any(|pred| { + pred.kind.in_where_clause() && pred.span.contains(span) + }) { let (span, separator) = if let [.., last] = bounds { (last.span().shrink_to_hi(), " +") diff --git a/tests/ui/dyn-compatibility/assoc_type_bounds_sized_used.stderr b/tests/ui/dyn-compatibility/assoc_type_bounds_sized_used.stderr index 9eb4e5bf5ef05..d33536e57ebe5 100644 --- a/tests/ui/dyn-compatibility/assoc_type_bounds_sized_used.stderr +++ b/tests/ui/dyn-compatibility/assoc_type_bounds_sized_used.stderr @@ -19,10 +19,6 @@ help: consider removing the `?Sized` bound to make the type parameter `Sized` LL - fn bop() { LL + fn bop() { | -help: consider relaxing the implicit `Sized` restriction - | -LL | type Bar: Default + ?Sized - | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-diagnostics.rs b/tests/ui/traits/next-solver/assoc-item-own-bound-diagnostics.rs new file mode 100644 index 0000000000000..20cfbfb6fcffe --- /dev/null +++ b/tests/ui/traits/next-solver/assoc-item-own-bound-diagnostics.rs @@ -0,0 +1,92 @@ +//@ compile-flags: -Znext-solver + +// Tests the diagnostics for associated-item own clauses (e.g. `type Assoc +// where Self: Sized;`) that are not satisfied by an impl. The recovered error +// should point at the type that fails the clause and only suggest fixes that +// would actually satisfy it. + +// The impl associated type already carries other where clauses, but is +// missing the `Self: Sized` clause required by the trait item. Relaxing the +// implicit `Sized` restriction of the associated type would not satisfy that +// clause. +trait TraitWithBound { + type Assoc + where + Self: Sized, + Self: 'static; +} + +impl TraitWithBound for T { + //~^ ERROR the size for values of type `T` cannot be known at compilation time + type Assoc = i32 + where + Self: 'static; +} + +// The value of the impl associated type does not satisfy the implicit `Sized` +// restriction of the trait item. Relaxing that restriction is the right fix, +// so the suggestion must still be emitted even though the trait item carries +// other explicit where clauses. +trait TraitWithOtherClause { + type Assoc + where + Self: 'static; +} + +impl TraitWithOtherClause for () { + type Assoc = [u8]; + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time +} + +// A trait item without any explicit where clauses: the ordinary +// implicit-`Sized` diagnostic is unchanged. +trait NormalAssoc { + type Assoc; +} + +impl NormalAssoc for () { + type Assoc = [u8]; + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time +} + +// A projection use site outside of an impl: the recovery also applies there. +trait NonImplTrait { + type Assoc + where + Self: Sized; +} + +fn non_impl_use() { + let _: ::Assoc; + //~^ ERROR the size for values of type `T` cannot be known at compilation time + //~| ERROR the trait bound `T: NonImplTrait` is not satisfied +} + +// Own clauses with traits other than `Sized` are handled the same way. +trait Marker {} + +trait TraitWithMarker { + type Assoc + where + Self: Marker; +} + +impl TraitWithMarker for T { + //~^ ERROR the trait bound `T: Marker` is not satisfied + type Assoc = i32; +} + +// Own clauses on traits with a custom `#[rustc_on_unimplemented]` diagnostic +// keep their custom label. +trait TraitWithSend { + type Assoc + where + Self: Send; +} + +impl TraitWithSend for T { + //~^ ERROR `T` cannot be sent between threads safely + type Assoc = i32; +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.stderr b/tests/ui/traits/next-solver/assoc-item-own-bound-diagnostics.stderr similarity index 68% rename from tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.stderr rename to tests/ui/traits/next-solver/assoc-item-own-bound-diagnostics.stderr index dcca095ce7717..26e62981a6b4e 100644 --- a/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.stderr +++ b/tests/ui/traits/next-solver/assoc-item-own-bound-diagnostics.stderr @@ -1,14 +1,17 @@ error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:11:6 + --> $DIR/assoc-item-own-bound-diagnostics.rs:19:6 | LL | impl TraitWithBound for T { - | ^ this type parameter needs to be `Sized` + | ^ + | | + | doesn't have a size known at compile-time + | this type parameter needs to be `Sized` | note: required by a bound in `TraitWithBound::Assoc` - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:7:15 + --> $DIR/assoc-item-own-bound-diagnostics.rs:15:15 | +LL | trait TraitWithBound { LL | type Assoc - | ----- required by a bound in this associated type LL | where LL | Self: Sized, | ^^^^^ required by this bound in `TraitWithBound::Assoc` @@ -17,21 +20,38 @@ help: consider removing the `?Sized` bound to make the type parameter `Sized` LL - impl TraitWithBound for T { LL + impl TraitWithBound for T { | -help: consider adding `where Self: Sized` + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/assoc-item-own-bound-diagnostics.rs:37:18 + | +LL | type Assoc = [u8]; + | ^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `TraitWithOtherClause::Assoc` + --> $DIR/assoc-item-own-bound-diagnostics.rs:31:5 + | +LL | trait TraitWithOtherClause { +LL | / type Assoc +LL | | where +LL | | Self: 'static; + | |______________________^ required by this bound in `TraitWithOtherClause::Assoc` +help: consider relaxing the implicit `Sized` restriction | -LL | Self: 'static, Self: Sized; - | +++++++++++++ +LL | type Assoc: ?Sized + | ++++++++ error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:24:18 + --> $DIR/assoc-item-own-bound-diagnostics.rs:48:18 | LL | type Assoc = [u8]; | ^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[u8]` note: required by a bound in `NormalAssoc::Assoc` - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:20:5 + --> $DIR/assoc-item-own-bound-diagnostics.rs:44:5 | +LL | trait NormalAssoc { LL | type Assoc; | ^^^^^^^^^^^ required by this bound in `NormalAssoc::Assoc` help: consider relaxing the implicit `Sized` restriction @@ -40,16 +60,16 @@ LL | type Assoc: ?Sized; | ++++++++ error[E0277]: the trait bound `T: Marker` is not satisfied - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:50:6 + --> $DIR/assoc-item-own-bound-diagnostics.rs:74:6 | LL | impl TraitWithMarker for T { | ^ the trait `Marker` is not implemented for `T` | note: required by a bound in `TraitWithMarker::Assoc` - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:47:15 + --> $DIR/assoc-item-own-bound-diagnostics.rs:71:15 | +LL | trait TraitWithMarker { LL | type Assoc - | ----- required by a bound in this associated type LL | where LL | Self: Marker; | ^^^^^^ required by this bound in `TraitWithMarker::Assoc` @@ -59,16 +79,16 @@ LL | impl TraitWithMarker for T { | ++++++++ error[E0277]: `T` cannot be sent between threads safely - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:62:6 + --> $DIR/assoc-item-own-bound-diagnostics.rs:87:6 | LL | impl TraitWithSend for T { | ^ `T` cannot be sent between threads safely | note: required by a bound in `TraitWithSend::Assoc` - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:59:15 + --> $DIR/assoc-item-own-bound-diagnostics.rs:84:15 | +LL | trait TraitWithSend { LL | type Assoc - | ----- required by a bound in this associated type LL | where LL | Self: Send; | ^^^^ required by this bound in `TraitWithSend::Assoc` @@ -78,7 +98,7 @@ LL | impl TraitWithSend for T { | +++++++++++++++++++ error[E0277]: the trait bound `T: NonImplTrait` is not satisfied - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:36:13 + --> $DIR/assoc-item-own-bound-diagnostics.rs:60:13 | LL | let _: ::Assoc; | ^ the trait `NonImplTrait` is not implemented for `T` @@ -89,7 +109,7 @@ LL | fn non_impl_use() { | ++++++++++++++ error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:36:13 + --> $DIR/assoc-item-own-bound-diagnostics.rs:60:13 | LL | fn non_impl_use() { | - this type parameter needs to be `Sized` @@ -97,10 +117,10 @@ LL | let _: ::Assoc; | ^ doesn't have a size known at compile-time | note: required by a bound in `NonImplTrait::Assoc` - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:32:15 + --> $DIR/assoc-item-own-bound-diagnostics.rs:56:15 | +LL | trait NonImplTrait { LL | type Assoc - | ----- required by a bound in this associated type LL | where LL | Self: Sized; | ^^^^^ required by this bound in `NonImplTrait::Assoc` @@ -109,11 +129,7 @@ help: consider removing the `?Sized` bound to make the type parameter `Sized` LL - fn non_impl_use() { LL + fn non_impl_use() { | -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assoc: ?Sized - | ++++++++ -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs b/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs deleted file mode 100644 index f9553fbee821d..0000000000000 --- a/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs +++ /dev/null @@ -1,67 +0,0 @@ -//@ compile-flags: -Znext-solver - -// Case B: implementation associated type already has an existing where-clause -trait TraitWithBound { - type Assoc - where - Self: Sized, - Self: 'static; -} - -impl TraitWithBound for T { - //~^ ERROR the size for values of type `T` cannot be known at compilation time - type Assoc = i32 - where - Self: 'static; -} - -// Case C: normal associated-type implicit-Sized diagnostic (suppression is not global) -trait NormalAssoc { - type Assoc; -} - -impl NormalAssoc for () { - type Assoc = [u8]; - //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time -} - -// Case D: projection/non-impl use-site (targeted suggestion does not leak) -trait NonImplTrait { - type Assoc - where - Self: Sized; -} - -fn non_impl_use() { - let _: ::Assoc; - //~^ ERROR the size for values of type `T` cannot be known at compilation time - //~| ERROR the trait bound `T: NonImplTrait` is not satisfied -} - -// Case E: explicit associated-type bound with a trait other than `Sized` -trait Marker {} - -trait TraitWithMarker { - type Assoc - where - Self: Marker; -} - -impl TraitWithMarker for T { - //~^ ERROR the trait bound `T: Marker` is not satisfied - type Assoc = i32; -} - -// Case F: associated-type bound with a custom on-unimplemented diagnostic -trait TraitWithSend { - type Assoc - where - Self: Send; -} - -impl TraitWithSend for T { - //~^ ERROR `T` cannot be sent between threads safely - type Assoc = i32; -} - -fn main() {} diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.current.stderr b/tests/ui/traits/next-solver/assoc-item-own-bound.current.stderr similarity index 90% rename from tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.current.stderr rename to tests/ui/traits/next-solver/assoc-item-own-bound.current.stderr index 00378f33a5597..5f60362ef64a0 100644 --- a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.current.stderr +++ b/tests/ui/traits/next-solver/assoc-item-own-bound.current.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/assoc-item-own-bound-issue-162368.rs:13:18 + --> $DIR/assoc-item-own-bound.rs:18:18 | LL | impl Trait for T { | - this type parameter needs to be `Sized` diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.next.stderr b/tests/ui/traits/next-solver/assoc-item-own-bound.next.stderr similarity index 61% rename from tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.next.stderr rename to tests/ui/traits/next-solver/assoc-item-own-bound.next.stderr index c2a9e40177178..4831cf72da63f 100644 --- a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.next.stderr +++ b/tests/ui/traits/next-solver/assoc-item-own-bound.next.stderr @@ -1,14 +1,17 @@ error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/assoc-item-own-bound-issue-162368.rs:12:6 + --> $DIR/assoc-item-own-bound.rs:17:6 | LL | impl Trait for T { - | ^ this type parameter needs to be `Sized` + | ^ + | | + | doesn't have a size known at compile-time + | this type parameter needs to be `Sized` | note: required by a bound in `Trait::Assoc` - --> $DIR/assoc-item-own-bound-issue-162368.rs:8:15 + --> $DIR/assoc-item-own-bound.rs:13:15 | +LL | trait Trait { LL | type Assoc - | ----- required by a bound in this associated type LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Trait::Assoc` @@ -17,10 +20,6 @@ help: consider removing the `?Sized` bound to make the type parameter `Sized` LL - impl Trait for T { LL + impl Trait for T { | -help: consider adding `where Self: Sized` - | -LL | type Assoc = i32 where Self: Sized; - | +++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.rs b/tests/ui/traits/next-solver/assoc-item-own-bound.rs similarity index 61% rename from tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.rs rename to tests/ui/traits/next-solver/assoc-item-own-bound.rs index b6356c9b72d76..17275775c704c 100644 --- a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.rs +++ b/tests/ui/traits/next-solver/assoc-item-own-bound.rs @@ -2,6 +2,11 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver +// Regression test for rust-lang/rust#162368: an impl associated type that does +// not satisfy an explicit own clause of the trait item used to be reported as +// a projection type mismatch (E0271) by the new solver instead of the +// unsatisfied trait bound (E0277). + trait Trait { type Assoc where diff --git a/tests/ui/traits/trivial-unsized-projection-2.bad.stderr b/tests/ui/traits/trivial-unsized-projection-2.bad.stderr index 16f1ceaf7e67f..3b5efa6b73523 100644 --- a/tests/ui/traits/trivial-unsized-projection-2.bad.stderr +++ b/tests/ui/traits/trivial-unsized-projection-2.bad.stderr @@ -18,10 +18,6 @@ LL | type Assert LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection-2.rs:22:12 @@ -44,10 +40,6 @@ LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr b/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr index b378a7406ecf5..0eb9e23a0e7ce 100644 --- a/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr +++ b/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr @@ -18,10 +18,6 @@ LL | type Assert LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection-2.rs:22:36 @@ -31,22 +27,18 @@ LL | const FOO: ::Assert = todo!(); | = help: within `Tail`, the trait `Sized` is not implemented for `[()]` note: required because it appears within the type `Tail` - --> $DIR/trivial-unsized-projection-2.rs:17:8 + --> $DIR/trivial-unsized-projection-2.rs:17:13 | LL | struct Tail([()]); - | ^^^^ + | ^^^^ required by this field note: required by a bound in `Bad::Assert` --> $DIR/trivial-unsized-projection-2.rs:14:15 | +LL | trait Bad { LL | type Assert - | ------ required by a bound in this associated type LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/traits/trivial-unsized-projection.bad.stderr b/tests/ui/traits/trivial-unsized-projection.bad.stderr index bab0a72587320..d003d8017151c 100644 --- a/tests/ui/traits/trivial-unsized-projection.bad.stderr +++ b/tests/ui/traits/trivial-unsized-projection.bad.stderr @@ -13,10 +13,6 @@ LL | type Assert LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection.rs:20:12 @@ -34,10 +30,6 @@ LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/traits/trivial-unsized-projection.bad_new.stderr b/tests/ui/traits/trivial-unsized-projection.bad_new.stderr index d5d829285305a..d7d3032c40d9d 100644 --- a/tests/ui/traits/trivial-unsized-projection.bad_new.stderr +++ b/tests/ui/traits/trivial-unsized-projection.bad_new.stderr @@ -13,10 +13,6 @@ LL | type Assert LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection.rs:20:36 @@ -28,15 +24,11 @@ LL | const FOO: <[()] as Bad>::Assert = todo!(); note: required by a bound in `Bad::Assert` --> $DIR/trivial-unsized-projection.rs:14:15 | +LL | trait Bad { LL | type Assert - | ------ required by a bound in this associated type LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error: aborting due to 2 previous errors From 35b8ebcf69676f7887902a32dfb1201090c51371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 12 Oct 2026 19:25:19 +0000 Subject: [PATCH 03/17] Point at assoc item for trait that needs to be implemented --- compiler/rustc_hir_typeck/src/diagnostics.rs | 2 +- .../rustc_hir_typeck/src/method/suggest.rs | 10 +++++++++- .../associated-const-no-item.stderr | 2 ++ .../inherent-impls-self-replacement.stderr | 6 ++++++ .../custom-on-unimplemented-diagnostic.stderr | 2 ++ .../functions-closures/fn-help-with-err.stderr | 3 +++ ...hod-unsatisfied-assoc-type-predicate.stderr | 2 ++ ...fn-ptr-impl-not-general-enough-57936.stderr | 3 +++ .../hrtb-doesnt-borrow-self-1.stderr | 3 +++ .../hrtb-doesnt-borrow-self-2.stderr | 2 ++ ..._inherent_impl_on_rigid_type.current.stderr | 2 ++ ...hod_on_inherent_impl_ref-err.current.stderr | 2 ++ ...method_on_inherent_impl_ref-err.next.stderr | 2 ++ ...ue-21659-show-relevant-trait-impls-3.stderr | 2 ++ .../would-constrain-opaque.current.stderr | 4 ++++ .../no-method-suggested-traits.stderr | 18 ++++++++++++++++++ .../iterator-does-not-need-into-iter.stderr | 2 ++ .../tuple-suggestions-issue-142488.stderr | 4 ++++ tests/ui/methods/vec-autoderef-autoref.stderr | 11 +++++++++++ tests/ui/nll/issue-57362-1.stderr | 2 ++ tests/ui/nll/issue-57362-2.stderr | 3 +++ .../issue-57642-higher-ranked-subtype.stderr | 6 ++++++ ...ult-impl-not-a-candidate-issue-48515.stderr | 2 ++ .../default-impl-not-an-impl.stderr | 2 ++ ...specialization-trait-not-implemented.stderr | 2 ++ ...plicitly-unimplemented-error-message.stderr | 1 + .../trait-upcasting/subtrait-method.stderr | 10 ++++++++++ .../trivial-bounds/trivial-bounds-leak.stderr | 2 ++ .../ui/use/unused-trait-with-method-err.stderr | 2 ++ 29 files changed, 112 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/diagnostics.rs b/compiler/rustc_hir_typeck/src/diagnostics.rs index 0b4a88500aaaa..3f06ce69b7c87 100644 --- a/compiler/rustc_hir_typeck/src/diagnostics.rs +++ b/compiler/rustc_hir_typeck/src/diagnostics.rs @@ -894,7 +894,7 @@ pub(crate) struct NoAssociatedItem<'tcx> { )] pub(crate) struct CandidateTraitNote { #[primary_span] - pub span: Span, + pub span: MultiSpan, pub trait_name: String, pub item_name: Ident, pub action_or_ty: String, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 53b081962a386..2dbeb64270c4c 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -4735,8 +4735,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if impls_trait(trait_info.def_id) { self.suggest_valid_traits(err, item_name, vec![trait_info.def_id], false); } else { + let mut span: MultiSpan = self.tcx.def_span(trait_info.def_id).into(); + for assoc in self + .tcx + .associated_items(trait_info.def_id) + .filter_by_name_unhygienic(item_name.name) + { + span.push_span_context(self.tcx.def_span(assoc.def_id).shrink_to_lo()); + } err.subdiagnostic(CandidateTraitNote { - span: self.tcx.def_span(trait_info.def_id), + span, trait_name: self.tcx.def_path_str(trait_info.def_id), item_name, action_or_ty: if trait_missing_method { diff --git a/tests/ui/associated-consts/associated-const-no-item.stderr b/tests/ui/associated-consts/associated-const-no-item.stderr index 40cd7791399b0..2cb3a0c33308f 100644 --- a/tests/ui/associated-consts/associated-const-no-item.stderr +++ b/tests/ui/associated-consts/associated-const-no-item.stderr @@ -10,6 +10,8 @@ note: `Foo` defines an item `ID`, perhaps you need to implement it | LL | trait Foo { | ^^^^^^^^^ +LL | const ID: i32; + | error: aborting due to 1 previous error diff --git a/tests/ui/delegation/inherent-impls-self-replacement.stderr b/tests/ui/delegation/inherent-impls-self-replacement.stderr index e017bee8af540..61eb8b363f359 100644 --- a/tests/ui/delegation/inherent-impls-self-replacement.stderr +++ b/tests/ui/delegation/inherent-impls-self-replacement.stderr @@ -109,6 +109,8 @@ note: `Trait` defines an item `get_s`, perhaps you need to implement it | LL | trait Trait: Sized { | ^^^^^^^^^^^^^^^^^^ +LL | fn get_s(self) -> S<(), 123>; + | error[E0599]: no method named `get_s` found for struct `Rc` in the current scope --> $DIR/inherent-impls-self-replacement.rs:49:38 @@ -122,6 +124,8 @@ note: `Trait` defines an item `get_s`, perhaps you need to implement it | LL | trait Trait: Sized { | ^^^^^^^^^^^^^^^^^^ +LL | fn get_s(self) -> S<(), 123>; + | error[E0599]: no method named `get_s` found for struct `Pin>` in the current scope --> $DIR/inherent-impls-self-replacement.rs:51:39 @@ -135,6 +139,8 @@ note: `Trait` defines an item `get_s`, perhaps you need to implement it | LL | trait Trait: Sized { | ^^^^^^^^^^^^^^^^^^ +LL | fn get_s(self) -> S<(), 123>; + | error[E0507]: cannot move out of `*self` which is behind a shared reference --> $DIR/inherent-impls-self-replacement.rs:24:34 diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.stderr index 1ba2c2e222dd0..1f34397c8a939 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.stderr @@ -26,6 +26,8 @@ note: `ProviderExt` defines an item `request`, perhaps you need to implement it | LL | pub trait ProviderExt { | ^^^^^^^^^^^^^^^^^^^^^ +LL | fn request(&self) { + | error: aborting due to 1 previous error diff --git a/tests/ui/functions-closures/fn-help-with-err.stderr b/tests/ui/functions-closures/fn-help-with-err.stderr index 9dc56c3652ac0..0540104d35620 100644 --- a/tests/ui/functions-closures/fn-help-with-err.stderr +++ b/tests/ui/functions-closures/fn-help-with-err.stderr @@ -16,6 +16,9 @@ note: `Bar` defines an item `bar`, perhaps you need to implement it | LL | trait Bar { | ^^^^^^^^^ +LL | +LL | fn bar(&self) {} + | help: use parentheses to call this closure | LL | arc2().bar(); diff --git a/tests/ui/generic-associated-types/method-unsatisfied-assoc-type-predicate.stderr b/tests/ui/generic-associated-types/method-unsatisfied-assoc-type-predicate.stderr index 61512dd46582d..24aabf2e8f898 100644 --- a/tests/ui/generic-associated-types/method-unsatisfied-assoc-type-predicate.stderr +++ b/tests/ui/generic-associated-types/method-unsatisfied-assoc-type-predicate.stderr @@ -20,6 +20,8 @@ note: `M` defines an item `f`, perhaps you need to implement it | LL | trait M { | ^^^^^^^ +LL | fn f(&self) {} + | error: aborting due to 1 previous error diff --git a/tests/ui/higher-ranked/hrtb-fn-ptr-impl-not-general-enough-57936.stderr b/tests/ui/higher-ranked/hrtb-fn-ptr-impl-not-general-enough-57936.stderr index f9862b8ab13a4..a90cedea04081 100644 --- a/tests/ui/higher-ranked/hrtb-fn-ptr-impl-not-general-enough-57936.stderr +++ b/tests/ui/higher-ranked/hrtb-fn-ptr-impl-not-general-enough-57936.stderr @@ -10,6 +10,9 @@ note: `X` defines an item `make_g`, perhaps you need to implement it | LL | trait X { | ^^^^^^^ +LL | type G; +LL | fn make_g() -> Self::G; + | error: implementation of `X` is not general enough --> $DIR/hrtb-fn-ptr-impl-not-general-enough-57936.rs:26:5 diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr index 21dc5c66adf2b..d3b95365efced 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr @@ -21,6 +21,9 @@ note: `StreamExt` defines an item `filterx`, perhaps you need to implement it | LL | pub trait StreamExt | ^^^^^^^^^^^^^^^^^^^ +... +LL | fn filterx(self, func: F) -> Filter + | error: aborting due to 1 previous error diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr index 91e65b2b07318..1b69dcb5788ec 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr @@ -21,6 +21,8 @@ note: `StreamExt` defines an item `countx`, perhaps you need to implement it | LL | pub trait StreamExt | ^^^^^^^^^^^^^^^^^^^ +... +LL | fn countx(mut self) -> usize = note: the full name for the type has been written to '$TEST_BUILD_DIR/hrtb-doesnt-borrow-self-2.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr index 3822bb5c9729e..2b1aca253d173 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr @@ -10,6 +10,8 @@ note: `MyDebug` defines an item `my_debug`, perhaps you need to implement it | LL | trait MyDebug { | ^^^^^^^^^^^^^ +LL | fn my_debug(&self); + | error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.current.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.current.stderr index f77c44118966b..6c3ed0efcd9f4 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.current.stderr +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.current.stderr @@ -13,6 +13,8 @@ note: `MyDebug` defines an item `my_debug`, perhaps you need to implement it | LL | trait MyDebug { | ^^^^^^^^^^^^^ +LL | fn my_debug(&self); + | error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.next.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.next.stderr index 71c588cd3a62f..94985e5e60b4e 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.next.stderr +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.next.stderr @@ -13,6 +13,8 @@ note: `MyDebug` defines an item `my_debug`, perhaps you need to implement it | LL | trait MyDebug { | ^^^^^^^^^^^^^ +LL | fn my_debug(&self); + | error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/issues/issue-21659-show-relevant-trait-impls-3.stderr b/tests/ui/impl-trait/issues/issue-21659-show-relevant-trait-impls-3.stderr index e5d7183937387..7834db7c5ff3c 100644 --- a/tests/ui/impl-trait/issues/issue-21659-show-relevant-trait-impls-3.stderr +++ b/tests/ui/impl-trait/issues/issue-21659-show-relevant-trait-impls-3.stderr @@ -13,6 +13,8 @@ note: `Foo` defines an item `foo`, perhaps you need to implement it | LL | trait Foo { | ^^^^^^^^^^^^ +LL | fn foo(&self, a: A) -> A { + | error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/method/would-constrain-opaque.current.stderr b/tests/ui/impl-trait/method/would-constrain-opaque.current.stderr index e29297ceea024..1e9a34b381192 100644 --- a/tests/ui/impl-trait/method/would-constrain-opaque.current.stderr +++ b/tests/ui/impl-trait/method/would-constrain-opaque.current.stderr @@ -10,6 +10,8 @@ note: `Trait` defines an item `method`, perhaps you need to implement it | LL | trait Trait: Sized { | ^^^^^^^^^^^^^^^^^^ +LL | fn method(self) {} + | error[E0599]: no method named `method` found for reference `&impl Sized` in the current scope --> $DIR/would-constrain-opaque.rs:32:11 @@ -23,6 +25,8 @@ note: `Trait` defines an item `method`, perhaps you need to implement it | LL | trait Trait: Sized { | ^^^^^^^^^^^^^^^^^^ +LL | fn method(self) {} + | error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/no-method-suggested-traits.stderr b/tests/ui/impl-trait/no-method-suggested-traits.stderr index ca383523f9238..1524679f8ba34 100644 --- a/tests/ui/impl-trait/no-method-suggested-traits.stderr +++ b/tests/ui/impl-trait/no-method-suggested-traits.stderr @@ -147,6 +147,9 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it | LL | pub trait Bar { | ^^^^^^^^^^^^^ +... +LL | fn method2(&self) {} + | error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&u64>>` in the current scope --> $DIR/no-method-suggested-traits.rs:50:44 @@ -160,6 +163,9 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it | LL | pub trait Bar { | ^^^^^^^^^^^^^ +... +LL | fn method2(&self) {} + | error[E0599]: no method named `method2` found for struct `no_method_suggested_traits::Foo` in the current scope --> $DIR/no-method-suggested-traits.rs:53:37 @@ -173,6 +179,9 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it | LL | pub trait Bar { | ^^^^^^^^^^^^^ +... +LL | fn method2(&self) {} + | error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&no_method_suggested_traits::Foo>>` in the current scope --> $DIR/no-method-suggested-traits.rs:55:71 @@ -186,6 +195,9 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it | LL | pub trait Bar { | ^^^^^^^^^^^^^ +... +LL | fn method2(&self) {} + | error[E0599]: no method named `method2` found for enum `no_method_suggested_traits::Bar` in the current scope --> $DIR/no-method-suggested-traits.rs:57:40 @@ -199,6 +211,9 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it | LL | pub trait Bar { | ^^^^^^^^^^^^^ +... +LL | fn method2(&self) {} + | error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&no_method_suggested_traits::Bar>>` in the current scope --> $DIR/no-method-suggested-traits.rs:59:74 @@ -212,6 +227,9 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it | LL | pub trait Bar { | ^^^^^^^^^^^^^ +... +LL | fn method2(&self) {} + | error[E0599]: no method named `method3` found for struct `Foo` in the current scope --> $DIR/no-method-suggested-traits.rs:62:9 diff --git a/tests/ui/iterators/iterator-does-not-need-into-iter.stderr b/tests/ui/iterators/iterator-does-not-need-into-iter.stderr index 3d3861e959f82..79810f761ee83 100644 --- a/tests/ui/iterators/iterator-does-not-need-into-iter.stderr +++ b/tests/ui/iterators/iterator-does-not-need-into-iter.stderr @@ -22,6 +22,8 @@ note: `HasMethod` defines an item `foo`, perhaps you need to implement it | LL | trait HasMethod { | ^^^^^^^^^^^^^^^ +LL | fn foo(self); + | error: aborting due to 1 previous error diff --git a/tests/ui/methods/tuple-suggestions-issue-142488.stderr b/tests/ui/methods/tuple-suggestions-issue-142488.stderr index f9363bb216f43..2ef7483770bf5 100644 --- a/tests/ui/methods/tuple-suggestions-issue-142488.stderr +++ b/tests/ui/methods/tuple-suggestions-issue-142488.stderr @@ -33,6 +33,8 @@ note: `Trait` defines an item `meth`, perhaps you need to implement it | LL | trait Trait { | ^^^^^^^^^^^ +LL | fn meth(self); + | error[E0599]: the method `f` exists for tuple `(_,)`, but its trait bounds were not satisfied --> $DIR/tuple-suggestions-issue-142488.rs:46:16 @@ -54,6 +56,8 @@ note: `Tr` defines an item `f`, perhaps you need to implement it | LL | trait Tr { | ^^^^^^^^ +LL | fn f(self) -> (A,) + | error: aborting due to 4 previous errors diff --git a/tests/ui/methods/vec-autoderef-autoref.stderr b/tests/ui/methods/vec-autoderef-autoref.stderr index 61c3bcc5b3b37..ecebce9430fc5 100644 --- a/tests/ui/methods/vec-autoderef-autoref.stderr +++ b/tests/ui/methods/vec-autoderef-autoref.stderr @@ -10,6 +10,8 @@ note: `MyIter` defines an item `test_mut`, perhaps you need to implement it | LL | trait MyIter { | ^^^^^^^^^^^^ +LL | fn test_mut(&mut self); + | help: there is a method `get_mut` with a similar name, but with different arguments --> $SRC_DIR/core/src/slice/mod.rs:LL:COL @@ -25,6 +27,9 @@ note: `MyIter` defines an item `test`, perhaps you need to implement it | LL | trait MyIter { | ^^^^^^^^^^^^ +LL | fn test_mut(&mut self); +LL | fn test(&self); + | error[E0599]: no method named `test` found for array `[{integer}; 1]` in the current scope --> $DIR/vec-autoderef-autoref.rs:9:11 @@ -38,6 +43,9 @@ note: `MyIter` defines an item `test`, perhaps you need to implement it | LL | trait MyIter { | ^^^^^^^^^^^^ +LL | fn test_mut(&mut self); +LL | fn test(&self); + | error[E0599]: no method named `test` found for reference `&[{integer}; 1]` in the current scope --> $DIR/vec-autoderef-autoref.rs:10:12 @@ -51,6 +59,9 @@ note: `MyIter` defines an item `test`, perhaps you need to implement it | LL | trait MyIter { | ^^^^^^^^^^^^ +LL | fn test_mut(&mut self); +LL | fn test(&self); + | error: aborting due to 4 previous errors diff --git a/tests/ui/nll/issue-57362-1.stderr b/tests/ui/nll/issue-57362-1.stderr index 79b1b131afa2c..892b9b7b30141 100644 --- a/tests/ui/nll/issue-57362-1.stderr +++ b/tests/ui/nll/issue-57362-1.stderr @@ -10,6 +10,8 @@ note: `Trait` defines an item `f`, perhaps you need to implement it | LL | trait Trait { | ^^^^^^^^^^^ +LL | fn f(self); + | error: aborting due to 1 previous error diff --git a/tests/ui/nll/issue-57362-2.stderr b/tests/ui/nll/issue-57362-2.stderr index 1b962b16bdc8f..913ae08efb788 100644 --- a/tests/ui/nll/issue-57362-2.stderr +++ b/tests/ui/nll/issue-57362-2.stderr @@ -10,6 +10,9 @@ note: `X` defines an item `make_g`, perhaps you need to implement it | LL | trait X { | ^^^^^^^ +LL | type G; +LL | fn make_g() -> Self::G; + | error: aborting due to 1 previous error diff --git a/tests/ui/nll/issue-57642-higher-ranked-subtype.stderr b/tests/ui/nll/issue-57642-higher-ranked-subtype.stderr index 0a56d1bc72031..dac3e57747ce1 100644 --- a/tests/ui/nll/issue-57642-higher-ranked-subtype.stderr +++ b/tests/ui/nll/issue-57642-higher-ranked-subtype.stderr @@ -10,6 +10,9 @@ note: `X` defines an item `make_g`, perhaps you need to implement it | LL | trait X { | ^^^^^^^ +LL | type G; +LL | fn make_g() -> Self::G; + | error[E0599]: no associated function or constant named `make_f` found for fn pointer `for<'a> fn(&'a ())` in the current scope --> $DIR/issue-57642-higher-ranked-subtype.rs:36:25 @@ -23,6 +26,9 @@ note: `Y` defines an item `make_f`, perhaps you need to implement it | LL | trait Y { | ^^^^^^^ +LL | type F; +LL | fn make_f() -> Self::F; + | error: aborting due to 2 previous errors diff --git a/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr b/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr index 91df5005d7095..7f82ae5e5a5c6 100644 --- a/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr +++ b/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr @@ -56,6 +56,8 @@ note: `Set` defines an item `contains`, perhaps you need to implement it | LL | trait Set { | ^^^^^^^^^^^^ +LL | fn contains(&self, bit: T); + | error: aborting due to 4 previous errors diff --git a/tests/ui/specialization/default-impl-not-an-impl.stderr b/tests/ui/specialization/default-impl-not-an-impl.stderr index cde757b3f98a7..c6dcafdd7bb06 100644 --- a/tests/ui/specialization/default-impl-not-an-impl.stderr +++ b/tests/ui/specialization/default-impl-not-an-impl.stderr @@ -33,6 +33,8 @@ note: `Foo` defines an item `f`, perhaps you need to implement it | LL | trait Foo { | ^^^^^^^^^ +LL | fn f(&self) -> u32; + | error[E0277]: the trait bound `u32: Foo` is not satisfied --> $DIR/default-impl-not-an-impl.rs:65:23 diff --git a/tests/ui/specialization/defaultimpl/specialization-trait-not-implemented.stderr b/tests/ui/specialization/defaultimpl/specialization-trait-not-implemented.stderr index 7cec84b1af6dd..bc3f1e1cce908 100644 --- a/tests/ui/specialization/defaultimpl/specialization-trait-not-implemented.stderr +++ b/tests/ui/specialization/defaultimpl/specialization-trait-not-implemented.stderr @@ -13,6 +13,8 @@ note: `Foo` defines an item `foo_one`, perhaps you need to implement it | LL | trait Foo { | ^^^^^^^^^ +LL | fn foo_one(&self) -> &'static str; + | error: aborting due to 1 previous error diff --git a/tests/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr b/tests/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr index b29442d7b8fb2..8bbdd2f75c538 100644 --- a/tests/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr +++ b/tests/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr @@ -45,6 +45,7 @@ note: `Foo` defines an item `foo`, perhaps you need to implement it | LL | trait Foo { | ^^^^^^^^^ +LL | fn foo(&self); = note: the trait `FooBar` defines an item `foo`, but is explicitly unimplemented error: aborting due to 4 previous errors diff --git a/tests/ui/traits/trait-upcasting/subtrait-method.stderr b/tests/ui/traits/trait-upcasting/subtrait-method.stderr index a16daa11d6f7a..9bfc3dfde0430 100644 --- a/tests/ui/traits/trait-upcasting/subtrait-method.stderr +++ b/tests/ui/traits/trait-upcasting/subtrait-method.stderr @@ -10,6 +10,8 @@ note: `Baz` defines an item `c`, perhaps you need to implement it | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ +LL | fn c(&self) -> i32 { + | help: there is a method `a` with a similar name | LL - bar.c(); @@ -28,6 +30,8 @@ note: `Bar` defines an item `b`, perhaps you need to implement it | LL | trait Bar: Foo { | ^^^^^^^^^^^^^^ +LL | fn b(&self) -> i32 { + | help: there is a method `a` with a similar name | LL - foo.b(); @@ -46,6 +50,8 @@ note: `Baz` defines an item `c`, perhaps you need to implement it | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ +LL | fn c(&self) -> i32 { + | help: there is a method `a` with a similar name | LL - foo.c(); @@ -64,6 +70,8 @@ note: `Bar` defines an item `b`, perhaps you need to implement it | LL | trait Bar: Foo { | ^^^^^^^^^^^^^^ +LL | fn b(&self) -> i32 { + | help: there is a method `a` with a similar name | LL - foo.b(); @@ -82,6 +90,8 @@ note: `Baz` defines an item `c`, perhaps you need to implement it | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ +LL | fn c(&self) -> i32 { + | help: there is a method `a` with a similar name | LL - foo.c(); diff --git a/tests/ui/trivial-bounds/trivial-bounds-leak.stderr b/tests/ui/trivial-bounds/trivial-bounds-leak.stderr index be472a5076913..b9882c74ad44f 100644 --- a/tests/ui/trivial-bounds/trivial-bounds-leak.stderr +++ b/tests/ui/trivial-bounds/trivial-bounds-leak.stderr @@ -19,6 +19,8 @@ note: `Foo` defines an item `test`, perhaps you need to implement it | LL | pub trait Foo { | ^^^^^^^^^^^^^ +LL | fn test(&self); + | error[E0277]: the trait bound `i32: Foo` is not satisfied --> $DIR/trivial-bounds-leak.rs:25:15 diff --git a/tests/ui/use/unused-trait-with-method-err.stderr b/tests/ui/use/unused-trait-with-method-err.stderr index 7ca4563673bb1..d748fddcf374a 100644 --- a/tests/ui/use/unused-trait-with-method-err.stderr +++ b/tests/ui/use/unused-trait-with-method-err.stderr @@ -13,6 +13,8 @@ note: `Bar` defines an item `uwu`, perhaps you need to implement it | LL | pub trait Bar { | ^^^^^^^^^^^^^ +LL | fn uwu(&self) {} + | error: aborting due to 1 previous error From bee7ba847e2aa00d428c818e875003928ad553fc Mon Sep 17 00:00:00 2001 From: ozankenangungor Date: Mon, 12 Oct 2026 23:48:29 +0300 Subject: [PATCH 04/17] Simplify associated type bound checks --- .../traits/fulfillment_errors.rs | 37 +++++++------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 000a4c1cdec43..fb527466d7fa4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -67,17 +67,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let ObligationCauseCode::WhereClause(item_def_id, span) = *cause_code.peel_derives() else { return false; }; - let is_impl_trait_assoc_type = if let Some(assoc_item) = - self.tcx.opt_associated_item(body_def_id.to_def_id()) - && let Some(trait_item_def_id) = assoc_item.trait_item_def_id() - { - trait_item_def_id == item_def_id - && matches!(assoc_item.kind, ty::AssocKind::Type { .. }) - } else { - false - }; - if !is_impl_trait_assoc_type { + if !self.tcx.opt_associated_item(body_def_id.to_def_id()).is_some_and(|assoc_item| { + assoc_item.trait_item_def_id().is_some_and(|trait_item_def_id| { + trait_item_def_id == item_def_id + && matches!(assoc_item.kind, ty::AssocKind::Type { .. }) + }) + }) { return false; } @@ -208,20 +204,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Err(guar) = leaf_trait_predicate.error_reported() { return guar; } - let impl_assoc_type_self_trait_bound_param = - if self.is_explicit_self_trait_bound_on_impl_assoc_type( - obligation.cause.body_def_id, - obligation.cause.code(), - leaf_trait_predicate.def_id(), - ) && let ty::Param(param_ty) = - *leaf_trait_predicate.skip_binder().self_ty().kind() - { - Some(param_ty) - } else { - None - }; - - if let Some(param_ty) = impl_assoc_type_self_trait_bound_param { + if self.is_explicit_self_trait_bound_on_impl_assoc_type( + obligation.cause.body_def_id, + obligation.cause.code(), + leaf_trait_predicate.def_id(), + ) && let ty::Param(param_ty) = + *leaf_trait_predicate.skip_binder().self_ty().kind() + { span = param_ty.span_from_generics( self.tcx, obligation.cause.body_def_id.to_def_id(), From 192d095f71e44f3ffb25ba36b8e645ceba0569a9 Mon Sep 17 00:00:00 2001 From: ozankenangungor Date: Tue, 13 Oct 2026 00:14:38 +0300 Subject: [PATCH 05/17] Document associated type guard --- .../src/error_reporting/traits/fulfillment_errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index fb527466d7fa4..b7f3aab985863 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -67,7 +67,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let ObligationCauseCode::WhereClause(item_def_id, span) = *cause_code.peel_derives() else { return false; }; - + // Continue only if there is an impl associated type corresponding to the expected trait item. if !self.tcx.opt_associated_item(body_def_id.to_def_id()).is_some_and(|assoc_item| { assoc_item.trait_item_def_id().is_some_and(|trait_item_def_id| { trait_item_def_id == item_def_id From fbf53be6d470bbf291d3e1b757c40e88469a1dba Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 12 Oct 2026 19:15:26 +0200 Subject: [PATCH 06/17] Test the interaction between `--remap-path-prefix` and doctests --- .../remap-path-prefix-doctest-panic/rmake.rs | 52 +++++++++++++++++++ .../src/doctest.rs | 4 ++ 2 files changed, 56 insertions(+) create mode 100644 tests/run-make/remap-path-prefix-doctest-panic/rmake.rs create mode 100644 tests/run-make/remap-path-prefix-doctest-panic/src/doctest.rs diff --git a/tests/run-make/remap-path-prefix-doctest-panic/rmake.rs b/tests/run-make/remap-path-prefix-doctest-panic/rmake.rs new file mode 100644 index 0000000000000..b184eda0b37bb --- /dev/null +++ b/tests/run-make/remap-path-prefix-doctest-panic/rmake.rs @@ -0,0 +1,52 @@ +// Checks that the location printed by a panicking doctest is remapped +// according to `--remap-path-scope`. +// +// Only the lines between the start of the stack backtrace and the end are +// are inspected, so timings, temp dirs and backtraces can't leak into the check. + +use run_make_support::assertion_helpers::{assert_contains, assert_not_contains}; +use run_make_support::rustdoc; + +/// Runs the failing doctest and returns stdout. +fn panic_file(scope: Option<&str>) -> String { + let mut cmd = rustdoc(); + cmd.input("src/doctest.rs") + .arg("--test") + .arg("--remap-path-prefix=src=/REMAPPED") + .arg("-Cdebuginfo=1") + .arg("--edition=2015") + .env("RUST_BACKTRACE", "1"); + + if let Some(scope) = scope { + cmd.arg(format!("--remap-path-scope={scope}")).arg("-Zunstable-options"); + } + + let out = cmd.run_fail(); + out.assert_stdout_contains("explicit panic"); + + let stdout = out.stdout_utf8(); + let section: Vec<&str> = stdout + .lines() + .skip_while(|l| l.trim() != "stack backtrace:") + .skip(1) + .take_while(|l| l.trim() != "failures:") + .collect(); + + assert!(!section.is_empty(), "no backtrace found in output:\n{stdout}"); + section.join("\n") +} + +fn main() { + // Check that with debuginfo (and related) we get the remapped path + for scope in [Some("debuginfo"), Some("object"), Some("all"), None] { + let stdout = panic_file(scope); + assert_contains(&stdout, "/REMAPPED/doctest.rs"); + assert_not_contains(&stdout, "src/doctest.rs"); + } + + for scope in ["diagnostics", "documentation", "macro"] { + let stdout = panic_file(Some(scope)); + assert_contains(&stdout, "src/doctest.rs"); + assert_not_contains(&stdout, "/REMAPPED/doctest.rs"); + } +} diff --git a/tests/run-make/remap-path-prefix-doctest-panic/src/doctest.rs b/tests/run-make/remap-path-prefix-doctest-panic/src/doctest.rs new file mode 100644 index 0000000000000..2160cc21f6b42 --- /dev/null +++ b/tests/run-make/remap-path-prefix-doctest-panic/src/doctest.rs @@ -0,0 +1,4 @@ +/// ``` +/// panic!() +/// ``` +pub fn f() {} From f8d4856c08ecbdc05e7766b12e809a90ee7ede22 Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 13 Oct 2026 20:47:57 +0200 Subject: [PATCH 07/17] Output remapped paths in rustdoc JSON with the `DOCUMENTATION` scope --- src/librustdoc/json/conversions.rs | 21 ++++++++----------- .../remap-path-prefix/debuginfo.rs | 5 +++++ tests/rustdoc-json/remap-path-prefix/diag.rs | 5 +++++ tests/rustdoc-json/remap-path-prefix/doc.rs | 5 +++++ .../rustdoc-json/remap-path-prefix/object.rs | 5 +++++ .../remap-path-prefix/remap-path-prefix.rs | 4 ++++ 6 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 tests/rustdoc-json/remap-path-prefix/debuginfo.rs create mode 100644 tests/rustdoc-json/remap-path-prefix/diag.rs create mode 100644 tests/rustdoc-json/remap-path-prefix/doc.rs create mode 100644 tests/rustdoc-json/remap-path-prefix/object.rs create mode 100644 tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 556a922f7044c..d23e9a8ca456a 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -17,7 +17,7 @@ use rustc_hir_pretty::PpAnn; use rustc_metadata::rendered_const; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::ModId; -use rustc_span::{Pos, Symbol, bug, kw, sym}; +use rustc_span::{Pos, RemapPathScopeComponents, Symbol, bug, kw, sym}; use rustdoc_json_types::*; use crate::clean::{self, ItemId}; @@ -191,17 +191,14 @@ impl FromClean for Option { fn from_clean(span: &clean::Span, renderer: &JsonRenderer<'_>) -> Self { match span.filename(renderer.sess()) { rustc_span::FileName::Real(name) => { - if let Some(local_path) = name.into_local_path() { - let hi = span.hi(renderer.sess()); - let lo = span.lo(renderer.sess()); - Some(Span { - filename: local_path, - begin: (lo.line, lo.col.to_usize() + 1), - end: (hi.line, hi.col.to_usize() + 1), - }) - } else { - None - } + let filename = name.path(RemapPathScopeComponents::DOCUMENTATION); + let hi = span.hi(renderer.sess()); + let lo = span.lo(renderer.sess()); + Some(Span { + filename: filename.to_path_buf(), + begin: (lo.line, lo.col.to_usize() + 1), + end: (hi.line, hi.col.to_usize() + 1), + }) } _ => None, } diff --git a/tests/rustdoc-json/remap-path-prefix/debuginfo.rs b/tests/rustdoc-json/remap-path-prefix/debuginfo.rs new file mode 100644 index 0000000000000..d378dca22be58 --- /dev/null +++ b/tests/rustdoc-json/remap-path-prefix/debuginfo.rs @@ -0,0 +1,5 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=/remapped +//@ compile-flags: --remap-path-scope=debuginfo -Zunstable-options + +//@ is "$.index[?(@.name=='StrLike')].span.filename" $FILE +pub trait StrLike {} diff --git a/tests/rustdoc-json/remap-path-prefix/diag.rs b/tests/rustdoc-json/remap-path-prefix/diag.rs new file mode 100644 index 0000000000000..79b469c40084e --- /dev/null +++ b/tests/rustdoc-json/remap-path-prefix/diag.rs @@ -0,0 +1,5 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=/remapped +//@ compile-flags: --remap-path-scope=diagnostics -Zunstable-options + +//@ is "$.index[?(@.name=='StrLike')].span.filename" $FILE +pub trait StrLike {} diff --git a/tests/rustdoc-json/remap-path-prefix/doc.rs b/tests/rustdoc-json/remap-path-prefix/doc.rs new file mode 100644 index 0000000000000..11fa449983754 --- /dev/null +++ b/tests/rustdoc-json/remap-path-prefix/doc.rs @@ -0,0 +1,5 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=/remapped +//@ compile-flags: --remap-path-scope=documentation -Zunstable-options + +//@ is "$.index[?(@.name=='StrLike')].span.filename" \"/remapped/remap-path-prefix/doc.rs\" +pub trait StrLike {} diff --git a/tests/rustdoc-json/remap-path-prefix/object.rs b/tests/rustdoc-json/remap-path-prefix/object.rs new file mode 100644 index 0000000000000..052ad7651eeab --- /dev/null +++ b/tests/rustdoc-json/remap-path-prefix/object.rs @@ -0,0 +1,5 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=/remapped +//@ compile-flags: --remap-path-scope=object -Zunstable-options + +//@ is "$.index[?(@.name=='StrLike')].span.filename" $FILE +pub trait StrLike {} diff --git a/tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs b/tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs new file mode 100644 index 0000000000000..1d0a1fa65fad0 --- /dev/null +++ b/tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs @@ -0,0 +1,4 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=/remapped + +//@ is "$.index[?(@.name=='StrLike')].span.filename" \"/remapped/remap-path-prefix/remap-path-prefix.rs\" +pub trait StrLike {} From c2a249de8feb848954f2e99d28d78139672b0265 Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 13 Oct 2026 21:57:52 +0200 Subject: [PATCH 08/17] Ignore cross-compile as doctests don't always work when cross-compiled --- tests/run-make/remap-path-prefix-doctest-panic/rmake.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/run-make/remap-path-prefix-doctest-panic/rmake.rs b/tests/run-make/remap-path-prefix-doctest-panic/rmake.rs index b184eda0b37bb..383955a270ab3 100644 --- a/tests/run-make/remap-path-prefix-doctest-panic/rmake.rs +++ b/tests/run-make/remap-path-prefix-doctest-panic/rmake.rs @@ -4,6 +4,8 @@ // Only the lines between the start of the stack backtrace and the end are // are inspected, so timings, temp dirs and backtraces can't leak into the check. +//@ ignore-cross-compile + use run_make_support::assertion_helpers::{assert_contains, assert_not_contains}; use run_make_support::rustdoc; From 0d0f40400243a926ab26953a5b089252c06035fc Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 13 Oct 2026 21:54:04 +0200 Subject: [PATCH 09/17] Add README.md and descriptions for the remap-path-prefix JSON tests --- tests/rustdoc-json/README.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/rustdoc-json/README.md diff --git a/tests/rustdoc-json/README.md b/tests/rustdoc-json/README.md new file mode 100644 index 0000000000000..875a149585010 --- /dev/null +++ b/tests/rustdoc-json/README.md @@ -0,0 +1,7 @@ +## `tests/rustdoc-json/remap-path-prefix` + +Tests for `--remap-path-prefix` and `--remap-path-scope` with rustdoc JSON: + - `remap-path-prefix`: only uses bare `--remap-path-prefix`, expects remapped paths + - `doc`: pass `--remap-path-prefix` and the `documentation` scope, expects remapped paths + - `object`: pass `--remap-path-prefix` and the `object` scope, expects un-remapped paths + - `debuginfo`: pass `--remap-path-prefix` and the `debuginfo` scope, expects un-remapped paths From 60ec7daab75c946fada8778a1871718a7c4e721b Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 14 Oct 2026 12:40:44 +1100 Subject: [PATCH 10/17] Move `thin_lto_buffer` to a OnceLock in ModuleLlvm This LTO buffer appears to be an implementation detail of the LLVM backend, as it is not touched by other in-tree backends. Using OnceLock makes it possible to set the buffer through a shared reference. This will be necessary when enforcing scoped access to the other contents of ModuleLlvm. --- .../rustc_codegen_llvm/src/back/llvm_backend.rs | 16 +++++++++++++++- compiler/rustc_codegen_llvm/src/back/lto.rs | 6 +++++- compiler/rustc_codegen_llvm/src/back/write.rs | 8 ++++++-- compiler/rustc_codegen_ssa/src/lib.rs | 16 ++-------------- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs index 8780af2f6d595..269540f268e8e 100644 --- a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -5,6 +5,7 @@ use std::any::Any; use std::ffi::CStr; use std::mem::ManuallyDrop; use std::path::PathBuf; +use std::sync::OnceLock; use rustc_ast::expand::allocator::AllocatorMethod; use rustc_codegen_ssa::back::lto::ThinModule; @@ -427,6 +428,12 @@ pub struct ModuleLlvm { // This field is `ManuallyDrop` because it is important that the `TargetMachine` // is disposed prior to the `Context` being disposed otherwise UAFs can occur. pub(crate) tm: ManuallyDrop, + + /// Saved ThinLTO buffer for embedding in the object file. + /// + /// This is currently a `OnceLock` to avoid aliasing problems when writing to it + /// from code that also wants to pass around a shared ref to `ModuleCodegen`. + pub(crate) thin_lto_buffer: OnceLock>, } unsafe impl Send for ModuleLlvm {} @@ -442,6 +449,7 @@ impl ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(create_target_machine(tcx, mod_name)), + thin_lto_buffer: OnceLock::new(), } } } @@ -455,6 +463,7 @@ impl ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)), + thin_lto_buffer: OnceLock::new(), } } } @@ -472,7 +481,12 @@ impl ModuleLlvm { let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx); let tm = tm_factory(dcx, TargetMachineFactoryConfig::new(cgcx, name.to_str().unwrap())); - ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) } + ModuleLlvm { + llmod_raw, + llcx, + tm: ManuallyDrop::new(tm), + thin_lto_buffer: OnceLock::new(), + } } } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index b171424b46245..90bdd2e42fea6 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -718,7 +718,11 @@ pub(crate) fn optimize_and_codegen_thin_module( let mut module = ModuleCodegen::new_regular(thin_module.name(), module_llvm); // Given that the newly created module lacks a thinlto buffer for embedding, we need to re-add it here. if cgcx.module_config.embed_bitcode() { - module.thin_lto_buffer = Some(thin_module.data().to_vec()); + module + .module_llvm + .thin_lto_buffer + .set(thin_module.data().to_vec()) + .expect("thin_lto_buffer should be unset"); } { let target = &*module.module_llvm.tm; diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index b7e400567b1f3..56b5e45b85a2f 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -937,7 +937,11 @@ pub(crate) fn optimize( }; if let Some(thin_lto_buffer) = thin_lto_buffer { let thin_lto_buffer = thin_lto_buffer.unwrap(); - module.thin_lto_buffer = Some(thin_lto_buffer.data().to_vec()); + module + .module_llvm + .thin_lto_buffer + .set(thin_lto_buffer.data().to_vec()) + .expect("thin_lto_buffer should be unset"); let bc_summary_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::ThinLinkBitcode, &module.name); if let Some(thin_lto_summary_buffer) = thin_lto_summary_buffer @@ -1025,7 +1029,7 @@ pub(crate) fn codegen( let _timer = prof .generic_activity_with_arg("LLVM_module_codegen_embed_bitcode", &*module.name); let thin_bc = - module.thin_lto_buffer.as_deref().expect("cannot find embedded bitcode"); + module.module_llvm.thin_lto_buffer.get().expect("cannot find embedded bitcode"); embed_bitcode(cgcx, llcx, llmod, &thin_bc); } } diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index af37694136d24..1ac9564db0149 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -65,27 +65,15 @@ pub struct ModuleCodegen { pub name: String, pub module_llvm: M, pub kind: ModuleKind, - /// Saving the ThinLTO buffer for embedding in the object file. - pub thin_lto_buffer: Option>, } impl ModuleCodegen { pub fn new_regular(name: impl Into, module: M) -> Self { - Self { - name: name.into(), - module_llvm: module, - kind: ModuleKind::Regular, - thin_lto_buffer: None, - } + Self { name: name.into(), module_llvm: module, kind: ModuleKind::Regular } } pub fn new_allocator(name: impl Into, module: M) -> Self { - Self { - name: name.into(), - module_llvm: module, - kind: ModuleKind::Allocator, - thin_lto_buffer: None, - } + Self { name: name.into(), module_llvm: module, kind: ModuleKind::Allocator } } pub fn into_compiled_module( From d3f7014e7ab83f9e1cabddb9e752a82e912fc431 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 9 Oct 2026 21:03:53 +1100 Subject: [PATCH 11/17] Use a callback to enforce scoped lifetimes in ModuleLlvm Codegen code that needs any of `llcx`/`llmod`/`tm` must now access them through a callback, to ensure that the `'ll` lifetimes are coherent and non-static, and don't accidentally escape. --- .../src/back/llvm_backend.rs | 50 +++++++++--- compiler/rustc_codegen_llvm/src/back/lto.rs | 71 +++++++++-------- compiler/rustc_codegen_llvm/src/back/write.rs | 76 +++++++++++-------- compiler/rustc_codegen_llvm/src/base.rs | 6 +- compiler/rustc_codegen_llvm/src/context.rs | 5 +- 5 files changed, 130 insertions(+), 78 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs index 269540f268e8e..6d0da50afc312 100644 --- a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -3,6 +3,7 @@ use std::any::Any; use std::ffi::CStr; +use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::path::PathBuf; use std::sync::OnceLock; @@ -60,11 +61,10 @@ impl ExtraBackendMethods for LlvmCodegenBackend { methods: &[AllocatorMethod], ) -> ModuleLlvm { let module_llvm = ModuleLlvm::new_metadata(tcx, module_name); - let cx = - SimpleCx::new(module_llvm.llmod(), &module_llvm.llcx, tcx.data_layout.pointer_size()); - unsafe { - allocator::codegen(tcx, cx, module_name, methods); - } + module_llvm.with_module_refs(|ModuleRefs { llcx, llmod, .. }| { + let cx = SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size()); + unsafe { allocator::codegen(tcx, cx, module_name, methods) }; + }); module_llvm } fn compile_codegen_unit( @@ -142,7 +142,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { module: &mut ModuleCodegen, config: &ModuleConfig, ) { - back::write::optimize(cgcx, prof, shared_emitter, module, config) + back::write::optimize(cgcx, prof, shared_emitter, module, config); } fn optimize_and_codegen_thin( cgcx: &CodegenContext, @@ -163,7 +163,9 @@ impl WriteBackendMethods for LlvmCodegenBackend { back::write::codegen(cgcx, prof, shared_emitter, module, config) } fn serialize_module(module: Self::Module, is_thin: bool) -> Self::ModuleBuffer { - back::lto::ModuleBuffer::new(module.llmod(), is_thin) + module.with_module_refs(|ModuleRefs { llmod, .. }| { + back::lto::ModuleBuffer::new(llmod, is_thin) + }) } } @@ -422,12 +424,12 @@ impl CodegenBackend for LlvmCodegenBackend { } pub struct ModuleLlvm { - pub(crate) llcx: &'static mut llvm::Context, + llcx: &'static mut llvm::Context, llmod_raw: *const llvm::Module, // This field is `ManuallyDrop` because it is important that the `TargetMachine` // is disposed prior to the `Context` being disposed otherwise UAFs can occur. - pub(crate) tm: ManuallyDrop, + tm: ManuallyDrop, /// Saved ThinLTO buffer for embedding in the object file. /// @@ -490,8 +492,21 @@ impl ModuleLlvm { } } - pub(crate) fn llmod(&self) -> &llvm::Module { - unsafe { &*self.llmod_raw } + /// Passes a [`ModuleRefs`] struct to a callback function, giving it scoped + /// access to the resources owned by this module. + /// + /// Passing the references to a callback ensures that they have coherent + /// `'ll` lifetimes, and that they can't escape the callback. + pub(crate) fn with_module_refs( + &self, + inner_fn: impl for<'ll> FnOnce(ModuleRefs<'ll>) -> R, + ) -> R { + inner_fn(ModuleRefs { + llcx: self.llcx, + llmod: unsafe { &*self.llmod_raw }, + tm: self.tm.raw(), + _ph: PhantomData, + }) } } @@ -503,3 +518,16 @@ impl Drop for ModuleLlvm { } } } + +/// Bundle of references provided by [`ModuleLlvm::with_module_refs`]. +#[derive(Clone, Copy)] +pub(crate) struct ModuleRefs<'ll> { + pub(crate) llcx: &'ll llvm::Context, + pub(crate) llmod: &'ll llvm::Module, + pub(crate) tm: &'ll llvm::TargetMachine, + /// Make sure `'ll` is invariant, even if no fields happen to require that. + /// + /// FIXME(Zalathar): Consider using `PhantomInvariantLifetime` when the API + /// for that is settled. + _ph: PhantomData &'ll ()>, +} diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 90bdd2e42fea6..6d7c406946127 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -24,6 +24,7 @@ use rustc_span::bug; use rustc_structures::SanitizerSet; use tracing::{debug, info}; +use crate::back::llvm_backend::ModuleRefs; use crate::back::write::{ self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, codegen, save_temp_bitcode, @@ -249,7 +250,9 @@ fn fat_lto( .enumerate() .filter(|&(_, module)| module.kind == ModuleKind::Regular) .map(|(i, module)| { - let cost = unsafe { llvm::LLVMRustModuleCost(module.module_llvm.llmod()) }; + let cost = module.module_llvm.with_module_refs(|ModuleRefs { llmod, .. }| unsafe { + llvm::LLVMRustModuleCost(llmod) + }); (cost, i) }) .max(); @@ -269,11 +272,7 @@ fn fat_lto( ModuleCodegen::new_regular(name.into_string().unwrap(), llvm_module) } }; - { - let (llcx, llmod) = { - let llvm = &module.module_llvm; - (&llvm.llcx, llvm.llmod()) - }; + module.module_llvm.with_module_refs(|ModuleRefs { llcx, llmod, .. }| { info!("using {:?} as a base module", module.name); // The linking steps below may produce errors and diagnostics within LLVM @@ -293,7 +292,9 @@ fn fat_lto( // way we know of to do that is to serialize them to a string and them parse // them later. Not great but hey, that's why it's "fat" LTO, right? for module in in_memory { - let buffer = ModuleBuffer::new(module.module_llvm.llmod(), false); + let buffer = module + .module_llvm + .with_module_refs(|ModuleRefs { llmod, .. }| ModuleBuffer::new(llmod, false)); let llmod_id = CString::new(&module.name[..]).unwrap(); serialized_modules.push((SerializedModule::Local(buffer), llmod_id)); } @@ -330,7 +331,7 @@ fn fat_lto( ); } save_temp_bitcode(cgcx, &module, "lto.after-restriction"); - } + }); module } @@ -584,7 +585,7 @@ pub(crate) fn run_pass_manager( cgcx: &CodegenContext, prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, - module: &mut ModuleCodegen, + module: &ModuleCodegen, thin: bool, ) { let _timer = prof.generic_activity_with_arg("LLVM_lto_optimize", &*module.name); @@ -612,11 +613,24 @@ pub(crate) fn run_pass_manager( if enable_ad { write::AutodiffStage::DuringAD } else { write::AutodiffStage::PostAD } }; - unsafe { - write::llvm_optimize( - cgcx, prof, dcx, module, None, None, config, opt_level, opt_stage, stage, - ); - } + let llvm_optimize = |opt_stage, stage| { + module.module_llvm.with_module_refs(|module_refs| unsafe { + write::llvm_optimize( + cgcx, + prof, + dcx, + module_refs, + None, + None, + config, + opt_level, + opt_stage, + stage, + ) + }); + }; + + llvm_optimize(opt_stage, stage); if enable_ad && !thin { let opt_stage = llvm::OptStage::FatLTO; @@ -624,17 +638,15 @@ pub(crate) fn run_pass_manager( if !config.autodiff.contains(&config::AutoDiff::NoPostopt) && config.autodiff_post_passes.as_deref() != Some("") { - unsafe { - write::llvm_optimize( - cgcx, prof, dcx, module, None, None, config, opt_level, opt_stage, stage, - ); - } + llvm_optimize(opt_stage, stage); } // This is the final IR, so people should be able to inspect the optimized autodiff output, // for manual inspection. if config.autodiff.contains(&config::AutoDiff::PrintModFinal) { - unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) }; + module.module_llvm.with_module_refs(|ModuleRefs { llmod, .. }| unsafe { + llvm::LLVMDumpModule(llmod) + }); } } @@ -715,7 +727,7 @@ pub(crate) fn optimize_and_codegen_thin_module( // crates but for locally codegened modules we may be able to reuse // that LLVM Context and Module. let module_llvm = ModuleLlvm::parse(cgcx, tm_factory, module_name, thin_module.data(), dcx); - let mut module = ModuleCodegen::new_regular(thin_module.name(), module_llvm); + let module = ModuleCodegen::new_regular(thin_module.name(), module_llvm); // Given that the newly created module lacks a thinlto buffer for embedding, we need to re-add it here. if cgcx.module_config.embed_bitcode() { module @@ -724,9 +736,7 @@ pub(crate) fn optimize_and_codegen_thin_module( .set(thin_module.data().to_vec()) .expect("thin_lto_buffer should be unset"); } - { - let target = &*module.module_llvm.tm; - let llmod = module.module_llvm.llmod(); + module.module_llvm.with_module_refs(|ModuleRefs { llmod, tm, .. }| { save_temp_bitcode(cgcx, &module, "thin-lto-input"); // Up next comes the per-module local analyses that we do for Thin LTO. @@ -739,9 +749,7 @@ pub(crate) fn optimize_and_codegen_thin_module( // bindings we've got (currently `PassWrapper.cpp`) { let _timer = prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name()); - unsafe { - llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target.raw()) - }; + unsafe { llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, tm) }; save_temp_bitcode(cgcx, &module, "thin-lto-after-rename"); } @@ -767,9 +775,8 @@ pub(crate) fn optimize_and_codegen_thin_module( { let _timer = prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name()); - if unsafe { - !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target.raw()) - } { + if unsafe { !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, tm) } + { write::llvm_err(dcx, LlvmError::PrepareThinLtoModule); } save_temp_bitcode(cgcx, &module, "thin-lto-after-import"); @@ -782,10 +789,10 @@ pub(crate) fn optimize_and_codegen_thin_module( // little differently. { info!("running thin lto passes over {}", module.name); - run_pass_manager(cgcx, prof, dcx, &mut module, true); + run_pass_manager(cgcx, prof, dcx, &module, true); save_temp_bitcode(cgcx, &module, "thin-lto-after-pm"); } - } + }); codegen(cgcx, prof, shared_emitter, module, &cgcx.module_config) } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 56b5e45b85a2f..41a04c637b231 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -26,6 +26,7 @@ use rustc_span::{BytePos, DUMMY_SP, InnerSpan, Pos, RemapPathScopeComponents}; use rustc_target::spec::{CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; use tracing::{debug, trace}; +use crate::back::llvm_backend::ModuleRefs; use crate::back::lto::{Buffer, ModuleBuffer}; use crate::back::owned_target_machine::OwnedTargetMachine; use crate::back::profiling::{ @@ -302,11 +303,10 @@ pub(crate) fn save_temp_bitcode( } fn write_bitcode_to_file(module: &ModuleLlvm, path: &Path) { - unsafe { - let path = path_to_c_string(&path); - let llmod = module.llmod(); - llvm::LLVMWriteBitcodeToFile(llmod, path.as_ptr()); - } + let path = path_to_c_string(&path); + module.with_module_refs(|ModuleRefs { llmod, .. }| { + unsafe { llvm::LLVMWriteBitcodeToFile(llmod, path.as_ptr()) }; + }); } /// In what context is a diagnostic handler being attached to a codegen unit? @@ -540,11 +540,11 @@ pub(crate) enum AutodiffStage { PostAD, } -pub(crate) unsafe fn llvm_optimize( +pub(crate) unsafe fn llvm_optimize<'ll>( cgcx: &CodegenContext, prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, - module: &ModuleCodegen, + module_refs: ModuleRefs<'ll>, thin_lto_buffer: Option<&mut Option>, thin_lto_summary_buffer: Option<&mut Option>, config: &ModuleConfig, @@ -552,6 +552,8 @@ pub(crate) unsafe fn llvm_optimize( opt_stage: llvm::OptStage, autodiff_stage: AutodiffStage, ) { + let ModuleRefs { llcx, llmod, tm, .. } = module_refs; + // Enzyme: // The whole point of compiler based AD is to differentiate optimized IR instead of unoptimized // source code. However, benchmarks show that optimizations increasing the code size @@ -747,8 +749,7 @@ pub(crate) unsafe fn llvm_optimize( if cgcx.target_is_like_gpu && config.offload.iter().any(|o| matches!(o, config::Offload::Device(_))) { - let cx = - SimpleCx::new(module.module_llvm.llmod(), module.module_llvm.llcx, cgcx.pointer_size); + let cx = SimpleCx::new(llmod, llcx, cgcx.pointer_size); for func in cx.get_functions() { let offload_kernel = "offload-kernel"; if attributes::has_string_attr(func, offload_kernel) { @@ -778,8 +779,8 @@ pub(crate) unsafe fn llvm_optimize( let result = unsafe { llvm::LLVMRustOptimize( - module.module_llvm.llmod(), - &*module.module_llvm.tm.raw(), + llmod, + tm, to_pass_builder_opt_level(opt_level), opt_stage, cgcx.use_linker_plugin_lto, @@ -827,8 +828,8 @@ pub(crate) unsafe fn llvm_optimize( // 1) Bundle device module into offload image device.bin (device TM) let ok = unsafe { llvm::RustOffloadWrapper::get_instance().llvm_rust_bundle_images( - module.module_llvm.llmod(), - module.module_llvm.tm.raw(), + llmod, + tm, device_out_c.as_c_str(), ) }; @@ -860,10 +861,8 @@ pub(crate) unsafe fn llvm_optimize( } let device_bin_c = path_to_c_string(device_pathbuf.as_path()); let ok = unsafe { - llvm::RustOffloadWrapper::get_instance().llvm_rust_offload_wrap_images( - module.module_llvm.llmod(), - device_bin_c.as_c_str(), - ) + llvm::RustOffloadWrapper::get_instance() + .llvm_rust_offload_wrap_images(llmod, device_bin_c.as_c_str()) }; if !ok { dcx.emit_err(crate::diagnostics::OffloadWrapImagesFailed); @@ -873,12 +872,28 @@ pub(crate) unsafe fn llvm_optimize( result.into_result().unwrap_or_else(|()| llvm_err(dcx, LlvmError::RunLlvmPasses)) } -// Unsafe due to LLVM calls. pub(crate) fn optimize( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, - module: &mut ModuleCodegen, + module: &ModuleCodegen, + config: &ModuleConfig, +) { + // Pass `module_refs` to an inner function to avoid having to indent the whole thing. + // + // It feels a bit odd to be passing both `module` and `module_refs` here, but it's + // unclear whether trying to avoid that would actually be an improvement. + module.module_llvm.with_module_refs(|module_refs| { + optimize_inner(cgcx, prof, shared_emitter, module, module_refs, config); + }); +} + +fn optimize_inner<'ll>( + cgcx: &CodegenContext, + prof: &SelfProfilerRef, + shared_emitter: &SharedEmitter, + module: &ModuleCodegen, + module_refs: ModuleRefs<'ll>, // Should be the refs from `module.module_llvm` config: &ModuleConfig, ) { let _timer = prof.generic_activity_with_arg("LLVM_module_optimize", &*module.name); @@ -886,9 +901,13 @@ pub(crate) fn optimize( let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); - let llcx = &*module.module_llvm.llcx; - let _handlers = - DiagnosticHandlers::new(cgcx, shared_emitter, llcx, module, CodegenDiagnosticsStage::Opt); + let _handlers = DiagnosticHandlers::new( + cgcx, + shared_emitter, + module_refs.llcx, + module, + CodegenDiagnosticsStage::Opt, + ); if module.kind == ModuleKind::Regular { save_temp_bitcode(cgcx, module, "no-opt"); @@ -926,7 +945,7 @@ pub(crate) fn optimize( cgcx, prof, dcx, - module, + module_refs, thin_lto_buffer.as_mut(), thin_lto_summary_buffer.as_mut(), config, @@ -978,10 +997,7 @@ pub(crate) fn codegen( let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); - { - let llmod = module.module_llvm.llmod(); - let llcx = &*module.module_llvm.llcx; - let tm = &*module.module_llvm.tm; + module.module_llvm.with_module_refs(|ModuleRefs { llcx, llmod, tm, .. }| { let _handlers = DiagnosticHandlers::new( cgcx, shared_emitter, @@ -1095,7 +1111,7 @@ pub(crate) fn codegen( }; write_output_file( dcx, - tm.raw(), + tm, config.no_builtins, llmod, &path, @@ -1128,7 +1144,7 @@ pub(crate) fn codegen( write_output_file( dcx, - tm.raw(), + tm, config.no_builtins, llmod, &obj_out, @@ -1155,7 +1171,7 @@ pub(crate) fn codegen( } record_llvm_cgu_instructions_stats(prof, &module.name, llmod); - } + }); // `.dwo` files are only emitted if: // diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 77cb37edec770..87eb157a8892f 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -93,8 +93,8 @@ pub(crate) fn compile_codegen_unit( }); // Instantiate monomorphizations without filling out definitions yet... let llvm_module = ModuleLlvm::new(tcx, cgu_name.as_str()); - { - let mut cx = CodegenCx::new(tcx, cgu, &llvm_module, needs_bitcode); + llvm_module.with_module_refs(|module_refs| { + let mut cx = CodegenCx::new(tcx, cgu, module_refs, needs_bitcode); // Declare and store globals shared by all offload kernels // @@ -213,7 +213,7 @@ pub(crate) fn compile_codegen_unit( if cx.sess().opts.debuginfo != DebugInfo::None { cx.debuginfo_finalize(); } - } + }); ModuleCodegen::new_regular(cgu_name.to_string(), llvm_module) } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 46196ab7d360f..f653f99cc5c29 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -34,6 +34,7 @@ use rustc_target::spec::{ use smallvec::SmallVec; use crate::abi::to_llvm_calling_convention; +use crate::back::llvm_backend::ModuleRefs; use crate::back::write::to_llvm_code_model; use crate::builder::gpu_offload::{OffloadGlobals, OffloadKernelGlobals}; use crate::callee::get_fn; @@ -600,7 +601,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { pub(crate) fn new( tcx: TyCtxt<'tcx>, codegen_unit: &'tcx CodegenUnit<'tcx>, - llvm_module: &'ll crate::ModuleLlvm, + module_refs: ModuleRefs<'ll>, bitcode_needed: bool, ) -> Self { // An interesting part of Windows which MSVC forces our hand on (and @@ -659,7 +660,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { let tls_model = to_llvm_tls_model(tcx.sess.tls_model()); - let (llcx, llmod) = (&*llvm_module.llcx, llvm_module.llmod()); + let ModuleRefs { llcx, llmod, .. } = module_refs; let coverage_cx = tcx.sess.instrument_coverage().then(coverageinfo::CguCoverageContext::new); From d005c6506be92e3f1f4a84ec3394169d0a7ddcb6 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 9 Oct 2026 23:12:20 +1100 Subject: [PATCH 12/17] Avoid `&'static mut` references to an owned `llvm::Context` --- .../src/back/llvm_backend.rs | 38 +++++++++++-------- compiler/rustc_codegen_llvm/src/context.rs | 15 +++++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 6 +-- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs index 6d0da50afc312..7aa1920ccf81d 100644 --- a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -6,6 +6,7 @@ use std::ffi::CStr; use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::path::PathBuf; +use std::ptr; use std::sync::OnceLock; use rustc_ast::expand::allocator::AllocatorMethod; @@ -424,7 +425,7 @@ impl CodegenBackend for LlvmCodegenBackend { } pub struct ModuleLlvm { - llcx: &'static mut llvm::Context, + llcx_raw: ptr::NonNull, llmod_raw: *const llvm::Module, // This field is `ManuallyDrop` because it is important that the `TargetMachine` @@ -441,15 +442,24 @@ pub struct ModuleLlvm { unsafe impl Send for ModuleLlvm {} unsafe impl Sync for ModuleLlvm {} +/// Shared helper to call [`llvm::LLVMContextCreate`] and then configure +/// [`llvm::LLVMContextSetDiscardValueNames`]. +fn create_context_and_set_fewer_names(fewer_names: bool) -> ptr::NonNull { + unsafe { + let llcx_raw = llvm::LLVMContextCreate(); + llvm::LLVMContextSetDiscardValueNames(llcx_raw.as_ref(), fewer_names.to_llvm_bool()); + llcx_raw + } +} + impl ModuleLlvm { pub(crate) fn new(tcx: TyCtxt<'_>, mod_name: &str) -> Self { unsafe { - let llcx = llvm::LLVMContextCreate(); - llvm::LLVMContextSetDiscardValueNames(llcx, tcx.sess.fewer_names().to_llvm_bool()); - let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; + let llcx_raw = create_context_and_set_fewer_names(tcx.sess.fewer_names()); + let llmod_raw = context::create_module(tcx, llcx_raw.as_ref(), mod_name); ModuleLlvm { llmod_raw, - llcx, + llcx_raw, tm: ManuallyDrop::new(create_target_machine(tcx, mod_name)), thin_lto_buffer: OnceLock::new(), } @@ -458,12 +468,11 @@ impl ModuleLlvm { pub(crate) fn new_metadata(tcx: TyCtxt<'_>, mod_name: &str) -> Self { unsafe { - let llcx = llvm::LLVMContextCreate(); - llvm::LLVMContextSetDiscardValueNames(llcx, tcx.sess.fewer_names().to_llvm_bool()); - let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; + let llcx_raw = create_context_and_set_fewer_names(tcx.sess.fewer_names()); + let llmod_raw = context::create_module(tcx, llcx_raw.as_ref(), mod_name); ModuleLlvm { llmod_raw, - llcx, + llcx_raw, tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)), thin_lto_buffer: OnceLock::new(), } @@ -478,14 +487,13 @@ impl ModuleLlvm { dcx: DiagCtxtHandle<'_>, ) -> Self { unsafe { - let llcx = llvm::LLVMContextCreate(); - llvm::LLVMContextSetDiscardValueNames(llcx, cgcx.fewer_names.to_llvm_bool()); - let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx); + let llcx_raw = create_context_and_set_fewer_names(cgcx.fewer_names); + let llmod_raw = back::lto::parse_module(llcx_raw.as_ref(), name, buffer, dcx); let tm = tm_factory(dcx, TargetMachineFactoryConfig::new(cgcx, name.to_str().unwrap())); ModuleLlvm { llmod_raw, - llcx, + llcx_raw, tm: ManuallyDrop::new(tm), thin_lto_buffer: OnceLock::new(), } @@ -502,7 +510,7 @@ impl ModuleLlvm { inner_fn: impl for<'ll> FnOnce(ModuleRefs<'ll>) -> R, ) -> R { inner_fn(ModuleRefs { - llcx: self.llcx, + llcx: unsafe { self.llcx_raw.as_ref() }, llmod: unsafe { &*self.llmod_raw }, tm: self.tm.raw(), _ph: PhantomData, @@ -514,7 +522,7 @@ impl Drop for ModuleLlvm { fn drop(&mut self) { unsafe { ManuallyDrop::drop(&mut self.tm); - llvm::LLVMContextDispose(&mut *(self.llcx as *mut _)); + llvm::LLVMContextDispose(self.llcx_raw); } } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index f653f99cc5c29..6e0681e203f1d 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -186,11 +186,16 @@ pub(crate) unsafe fn create_module<'ll>( tcx: TyCtxt<'_>, llcx: &'ll llvm::Context, mod_name: &str, -) -> &'ll llvm::Module { - let sess = tcx.sess; - let mod_name = SmallCStr::new(mod_name); - let llmod = unsafe { llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx) }; +) -> *const llvm::Module { + let mod_name_cstr = SmallCStr::new(mod_name); + let llmod_raw = + unsafe { llvm::LLVMModuleCreateWithNameInContext(mod_name_cstr.as_ptr(), llcx) }; + init_module(tcx, llcx, unsafe { &*llmod_raw }); + llmod_raw +} +fn init_module<'ll, 'tcx>(tcx: TyCtxt<'tcx>, llcx: &'ll llvm::Context, llmod: &'ll llvm::Module) { + let sess = tcx.sess; let cx = SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size()); let mut target_data_layout = sess.target.data_layout.to_string(); @@ -593,8 +598,6 @@ pub(crate) unsafe fn create_module<'ll>( }; llvm::add_module_flag_u32(llmod, merge_behavior, key, *value); } - - llmod } impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 1a1af50c2c025..a8ededf305a29 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -887,8 +887,8 @@ pub(crate) type GetSymbolsErrorCallback = unsafe extern "C" fn(*const c_char) -> unsafe extern "C" { // Create and destroy contexts. - pub(crate) fn LLVMContextCreate() -> &'static mut Context; - pub(crate) fn LLVMContextDispose(C: &'static mut Context); + pub(crate) fn LLVMContextCreate() -> ptr::NonNull; + pub(crate) fn LLVMContextDispose(C: ptr::NonNull); pub(crate) fn LLVMContextSetDiscardValueNames(C: &Context, Discard: Bool); pub(crate) fn LLVMGetMDKindIDInContext( C: &Context, @@ -908,7 +908,7 @@ unsafe extern "C" { pub(crate) fn LLVMModuleCreateWithNameInContext( ModuleID: *const c_char, C: &Context, - ) -> &Module; + ) -> *const Module; pub(crate) safe fn LLVMCloneModule(M: &Module) -> &Module; /// Data layout. See Module::getDataLayout. From 86d18170e47363c0030316eb9eb5d0393f719f50 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 10 Oct 2026 17:42:33 +0900 Subject: [PATCH 13/17] add tests for local binding resolution in move expressions --- tests/ui/move-expr/closure-local-bindings.rs | 77 +++++++++++ .../move-expr/closure-local-bindings.stderr | 125 ++++++++++++++++++ .../move-expr/closure-local-uninitialized.rs | 13 ++ .../closure-local-uninitialized.stderr | 12 ++ .../move-initializer-local-bindings.rs | 66 +++++++++ tests/ui/move-expr/name-resolution.rs | 6 +- tests/ui/move-expr/name-resolution.stderr | 12 ++ 7 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 tests/ui/move-expr/closure-local-bindings.rs create mode 100644 tests/ui/move-expr/closure-local-bindings.stderr create mode 100644 tests/ui/move-expr/closure-local-uninitialized.rs create mode 100644 tests/ui/move-expr/closure-local-uninitialized.stderr create mode 100644 tests/ui/move-expr/move-initializer-local-bindings.rs create mode 100644 tests/ui/move-expr/name-resolution.stderr diff --git a/tests/ui/move-expr/closure-local-bindings.rs b/tests/ui/move-expr/closure-local-bindings.rs new file mode 100644 index 0000000000000..878d458cc977a --- /dev/null +++ b/tests/ui/move-expr/closure-local-bindings.rs @@ -0,0 +1,77 @@ +//@ check-fail +//@ edition: 2024 + +#![allow(incomplete_features)] +#![feature(gen_blocks, move_expr)] + +macro_rules! move_value { + ($value:expr) => { move($value) }; +} + +fn main() { + let _ = |x: i32| move(x); + //~^ ERROR cannot use `x` in this move expression + + let _ = |(x, _): (i32, i32)| move(x); + //~^ ERROR cannot use `x` in this move expression + + let x = 1; + let _ = || { + let x = 2; + move(x); + //~^ ERROR cannot use `x` in this move expression + }; + + let _ = || { + for x in [1] { + move(x); + //~^ ERROR cannot use `x` in this move expression + } + }; + + let _ = || { + if let Some(x) = Some(1) { + move(x); + //~^ ERROR cannot use `x` in this move expression + } + }; + + let _ = || { + let x = 1; + || move(move(x)) + //~^ ERROR cannot use `x` in this move expression + }; + + let _ = || || move({ + let x = 1; + move(x) + //~^ ERROR cannot use `x` in this move expression + }); + + let _ = || { + let x = 1; + move_value!(x); + //~^ ERROR cannot use `x` in this move expression + }; + + let _ = async |x: i32| move(x); + //~^ ERROR cannot use `x` in this move expression + + let _ = async { + let x = 1; + move(x); + //~^ ERROR cannot use `x` in this move expression + }; + + let _ = gen { + let x = 1; + yield move(x); + //~^ ERROR cannot use `x` in this move expression + }; + + let _ = || { + let x = 1; + move(|| x) + //~^ ERROR cannot use `x` in this move expression + }; +} diff --git a/tests/ui/move-expr/closure-local-bindings.stderr b/tests/ui/move-expr/closure-local-bindings.stderr new file mode 100644 index 0000000000000..a762eeb5eb1e2 --- /dev/null +++ b/tests/ui/move-expr/closure-local-bindings.stderr @@ -0,0 +1,125 @@ +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:12:27 + | +LL | let _ = |x: i32| move(x); + | -------- -----^- + | || | + | || this expression is evaluated outside that scope + | |`x` is defined here + | `x` is defined within this scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:15:39 + | +LL | let _ = |(x, _): (i32, i32)| move(x); + | -------------------- -----^- + | | | | + | | | this expression is evaluated outside that scope + | | `x` is defined here + | `x` is defined within this scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:21:14 + | +LL | let _ = || { + | -- `x` is defined within this scope +LL | let x = 2; + | - `x` is defined here +LL | move(x); + | -----^- this expression is evaluated outside that scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:27:18 + | +LL | let _ = || { + | -- `x` is defined within this scope +LL | for x in [1] { + | - `x` is defined here +LL | move(x); + | -----^- this expression is evaluated outside that scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:34:18 + | +LL | let _ = || { + | -- `x` is defined within this scope +LL | if let Some(x) = Some(1) { + | - `x` is defined here +LL | move(x); + | -----^- this expression is evaluated outside that scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:41:22 + | +LL | let _ = || { + | -- `x` is defined within this scope +LL | let x = 1; + | - `x` is defined here +LL | || move(move(x)) + | -----^- this expression is evaluated outside that scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:47:14 + | +LL | let _ = || || move({ + | -- `x` is defined within this scope +LL | let x = 1; + | - `x` is defined here +LL | move(x) + | -----^- this expression is evaluated outside that scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:53:21 + | +LL | ($value:expr) => { move($value) }; + | ------------ this expression is evaluated outside that scope +... +LL | let _ = || { + | -- `x` is defined within this scope +LL | let x = 1; + | - `x` is defined here +LL | move_value!(x); + | ^ + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:57:33 + | +LL | let _ = async |x: i32| move(x); + | -------------- -----^- + | | | | + | | | this expression is evaluated outside that scope + | | `x` is defined here + | `x` is defined within this scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:62:14 + | +LL | let _ = async { + | ----- `x` is defined within this scope +LL | let x = 1; + | - `x` is defined here +LL | move(x); + | -----^- this expression is evaluated outside that scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:68:20 + | +LL | let _ = gen { + | --- `x` is defined within this scope +LL | let x = 1; + | - `x` is defined here +LL | yield move(x); + | -----^- this expression is evaluated outside that scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:74:17 + | +LL | let _ = || { + | -- `x` is defined within this scope +LL | let x = 1; + | - `x` is defined here +LL | move(|| x) + | --------^- this expression is evaluated outside that scope + +error: aborting due to 12 previous errors + diff --git a/tests/ui/move-expr/closure-local-uninitialized.rs b/tests/ui/move-expr/closure-local-uninitialized.rs new file mode 100644 index 0000000000000..bf5fd4364cbe3 --- /dev/null +++ b/tests/ui/move-expr/closure-local-uninitialized.rs @@ -0,0 +1,13 @@ +//@ check-fail +// Regression test for https://github.com/rust-lang/rust/issues/156509 + +#![allow(incomplete_features)] +#![feature(move_expr)] + +fn main() { + let _c = || { + let x; + move(x); + //~^ ERROR cannot use `x` in this move expression + }; +} diff --git a/tests/ui/move-expr/closure-local-uninitialized.stderr b/tests/ui/move-expr/closure-local-uninitialized.stderr new file mode 100644 index 0000000000000..a008c01fc38be --- /dev/null +++ b/tests/ui/move-expr/closure-local-uninitialized.stderr @@ -0,0 +1,12 @@ +error: cannot use `x` in this move expression + --> $DIR/closure-local-uninitialized.rs:10:14 + | +LL | let _c = || { + | -- `x` is defined within this scope +LL | let x; + | - `x` is defined here +LL | move(x); + | -----^- this expression is evaluated outside that scope + +error: aborting due to 1 previous error + diff --git a/tests/ui/move-expr/move-initializer-local-bindings.rs b/tests/ui/move-expr/move-initializer-local-bindings.rs new file mode 100644 index 0000000000000..000c39b5c23d9 --- /dev/null +++ b/tests/ui/move-expr/move-initializer-local-bindings.rs @@ -0,0 +1,66 @@ +//@ run-pass + +#![allow(incomplete_features, unused_variables)] +#![feature(move_expr)] + +fn main() { + let c = || move({ + let x = 3; + x + }); + assert_eq!(c(), 3); + + let c = || move({ + let x = 4; + (|| x)() + }); + assert_eq!(c(), 4); + + let outer = || { + let x = 5; + || move(x) + }; + assert_eq!(outer()(), 5); + + let outer = || { + let x = 6; + || move({ + let y = 7; + || move(y + x) + }) + }; + assert_eq!(outer()()(), 13); + + let c = || move({ + fn identity(x: i32) -> i32 { + x + } + identity(8) + }); + assert_eq!(c(), 8); + + let c = || move(const { 9 }); + assert_eq!(c(), 9); + + let x = 10; + let c = || move(x); + let d = || move(x + 1); + assert_eq!(c(), 10); + assert_eq!(d(), 11); + + let c = || { + let x = 12; + move({ + let x = 13; + x + }) + }; + assert_eq!(c(), 13); + + let c = |x: i32| move({ + let x; + x = 14; + x + }); + assert_eq!(c(0), 14); +} diff --git a/tests/ui/move-expr/name-resolution.rs b/tests/ui/move-expr/name-resolution.rs index 7c0886183eb36..0e4f44ed3b5ec 100644 --- a/tests/ui/move-expr/name-resolution.rs +++ b/tests/ui/move-expr/name-resolution.rs @@ -1,7 +1,4 @@ -//@ check-pass -//@ ignore-test (#155050): currently ICEs instead of reporting a name-resolution error -// FIXME(TaKO8Ki): Remove this ignore once closure-local names in `move(expr)` produce a real -// diagnostic instead of hitting the current `Res::Err` ICE path. +//@ check-fail #![allow(incomplete_features)] #![feature(move_expr)] @@ -9,5 +6,6 @@ fn main() { let _c = || { let x = 3; move(x); + //~^ ERROR cannot use `x` in this move expression }; } diff --git a/tests/ui/move-expr/name-resolution.stderr b/tests/ui/move-expr/name-resolution.stderr new file mode 100644 index 0000000000000..f82e3bc5de3f6 --- /dev/null +++ b/tests/ui/move-expr/name-resolution.stderr @@ -0,0 +1,12 @@ +error: cannot use `x` in this move expression + --> $DIR/name-resolution.rs:8:14 + | +LL | let _c = || { + | -- `x` is defined within this scope +LL | let x = 3; + | - `x` is defined here +LL | move(x); + | -----^- this expression is evaluated outside that scope + +error: aborting due to 1 previous error + From 13466179a09bccfc40e004d970811e141c6c8a69 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 10 Oct 2026 17:42:45 +0900 Subject: [PATCH 14/17] reject out-of-scope local bindings in move expressions --- .../rustc_resolve/src/diagnostics/impls.rs | 11 +++ compiler/rustc_resolve/src/diagnostics/mod.rs | 14 +++ compiler/rustc_resolve/src/ident.rs | 21 +++++ compiler/rustc_resolve/src/late.rs | 87 ++++++++++++++++--- compiler/rustc_resolve/src/lib.rs | 29 +++++-- 5 files changed, 144 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index f1400c83e4295..8ec9dbbd9ee2f 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -1436,6 +1436,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ResolutionError::BindingInNeverPattern => { self.dcx().create_err(diagnostics::BindingInNeverPattern { span }) } + ResolutionError::CannotUseInnerLocalInMoveExpr { + move_expr_span, + ident, + scope_span, + } => self.dcx().create_err(diagnostics::CannotUseInnerLocalInMoveExpr { + span, + ident, + binding_span: ident.span, + move_expr_span, + scope_span, + }), } } diff --git a/compiler/rustc_resolve/src/diagnostics/mod.rs b/compiler/rustc_resolve/src/diagnostics/mod.rs index a629af5d690bc..65ef919e9d0c7 100644 --- a/compiler/rustc_resolve/src/diagnostics/mod.rs +++ b/compiler/rustc_resolve/src/diagnostics/mod.rs @@ -525,6 +525,20 @@ pub(crate) struct BindingInNeverPattern { pub(crate) span: Span, } +#[derive(Diagnostic)] +#[diag("cannot use `{$ident}` in this move expression")] +pub(crate) struct CannotUseInnerLocalInMoveExpr { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, + #[label("`{$ident}` is defined here")] + pub(crate) binding_span: Span, + #[label("this expression is evaluated outside that scope")] + pub(crate) move_expr_span: Span, + #[label("`{$ident}` is defined within this scope")] + pub(crate) scope_span: Span, +} + #[derive(Diagnostic)] #[diag("duplicate definitions with name `{$name}`:", code = E0201)] pub(crate) struct TraitImplDuplicate { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 6a5a73715c840..6b460195b9b5b 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1511,9 +1511,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for rib in ribs { match rib.kind { + // `first_rib` marks the start of the closure or coroutine scope. + // The initializer is evaluated outside that scope, so bindings defined + // inside it are unavailable, while outer bindings remain accessible. + RibKind::MoveExpr { first_rib, move_expr_span, scope_span } + if rib_index >= first_rib => + { + if let Some(span) = finalize { + self.report_error( + span, + CannotUseInnerLocalInMoveExpr { + move_expr_span, + ident: original_rib_ident_def, + scope_span, + }, + ); + } + return Res::Err; + } RibKind::Normal | RibKind::Block(..) | RibKind::FnOrCoroutine + | RibKind::MoveExpr { .. } | RibKind::Module(..) | RibKind::MacroDefinition(..) | RibKind::ForwardGenericParamBan(_) => { @@ -1610,6 +1629,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { RibKind::Normal | RibKind::Block(..) | RibKind::FnOrCoroutine + | RibKind::MoveExpr { .. } | RibKind::Module(..) | RibKind::MacroDefinition(..) | RibKind::InlineAsmSym @@ -1717,6 +1737,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { RibKind::Normal | RibKind::Block(..) | RibKind::FnOrCoroutine + | RibKind::MoveExpr { .. } | RibKind::Module(..) | RibKind::MacroDefinition(..) | RibKind::InlineAsmSym diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 417b875297ce4..315ace9c0d4cf 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -219,6 +219,12 @@ pub(crate) enum RibKind<'ra> { /// We passed through a function, closure or coroutine signature. Disallow labels. FnOrCoroutine, + MoveExpr { + first_rib: usize, + move_expr_span: Span, + scope_span: Span, + }, + /// We passed through an item scope. Disallow upvars. Item(HasGenericParams, DefKind), @@ -264,6 +270,7 @@ impl RibKind<'_> { RibKind::Normal | RibKind::Block(..) | RibKind::FnOrCoroutine + | RibKind::MoveExpr { .. } | RibKind::ConstantItem(..) | RibKind::Module(_) | RibKind::MacroDefinition(_) @@ -840,6 +847,8 @@ struct LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { /// The current set of local scopes for types and values. ribs: PerNS>>, + move_expr_scopes: Vec>, + /// Previous popped `rib`, only used for diagnostic. last_block_rib: Option>, @@ -901,7 +910,9 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc // Always report errors in items we just entered. let old_ignore = replace(&mut self.in_func_body, false); with_owner(self, item.id, |this| { - this.with_lifetime_rib(LifetimeRibKind::Item, |this| this.resolve_item(item)) + this.with_move_expr_scope(None, |this| { + this.with_lifetime_rib(LifetimeRibKind::Item, |this| this.resolve_item(item)) + }) }); self.in_func_body = old_ignore; self.diag_metadata.current_item = prev; @@ -1617,6 +1628,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { type_ns: vec![Rib::new(start_rib_kind)], macro_ns: vec![Rib::new(start_rib_kind)], }, + move_expr_scopes: Vec::new(), last_block_rib: None, label_ribs: Vec::new(), lifetime_ribs: Vec::new(), @@ -1728,6 +1740,39 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } } + fn with_move_expr_scope( + &mut self, + scope_span: Option, + work: impl FnOnce(&mut Self) -> T, + ) -> T { + if !self.r.features.move_expr() { + return work(self); + } + let scope = scope_span.map(|span| (self.ribs[ValueNS].len(), span)); + self.move_expr_scopes.push(scope); + let result = work(self); + self.move_expr_scopes.pop(); + result + } + + fn with_move_expr_initializer( + &mut self, + move_expr_span: Span, + work: impl FnOnce(&mut Self) -> T, + ) -> T { + let Some(Some((first_rib, scope_span))) = self.move_expr_scopes.last().copied() else { + return work(self); + }; + self.move_expr_scopes.pop(); + let result = self.with_rib( + ValueNS, + RibKind::MoveExpr { first_rib, move_expr_span, scope_span }, + work, + ); + self.move_expr_scopes.push(Some((first_rib, scope_span))); + result + } + fn visit_generic_params(&mut self, params: &'ast [GenericParam], add_self_upper: bool) { // For type parameter defaults, we have to ban access // to following type parameters, as the GenericArgs can only @@ -5566,30 +5611,46 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ExprKind::Type(ref _type_expr, ref _ty) => { visit::walk_expr(self, expr); } + ExprKind::Move(..) => { + self.with_move_expr_initializer(expr.span, |this| visit::walk_expr(this, expr)); + } // For closures, RibKind::FnOrCoroutine is added in visit_fn ExprKind::Closure(ast::Closure { binder: ClosureBinder::For { ref generic_params, span }, + fn_decl_span, .. }) => { - self.with_generic_param_rib( - generic_params, - RibKind::Normal, - expr.id, - LifetimeBinderKind::Closure, - span, - |this| visit::walk_expr(this, expr), - ); + self.with_move_expr_scope(Some(fn_decl_span), |this| { + this.with_generic_param_rib( + generic_params, + RibKind::Normal, + expr.id, + LifetimeBinderKind::Closure, + span, + |this| visit::walk_expr(this, expr), + ); + }); + } + ExprKind::Closure(ref closure) => { + self.with_move_expr_scope(Some(closure.fn_decl_span), |this| { + visit::walk_expr(this, expr) + }); } - ExprKind::Closure(..) => visit::walk_expr(self, expr), - ExprKind::Gen(..) => { - self.with_label_rib(RibKind::FnOrCoroutine, |this| visit::walk_expr(this, expr)); + ExprKind::Gen(_, _, _, decl_span) => { + self.with_move_expr_scope(Some(decl_span), |this| { + this.with_label_rib(RibKind::FnOrCoroutine, |this| { + visit::walk_expr(this, expr) + }); + }); } ExprKind::Repeat(ref elem, ref ct) => { self.visit_expr(elem); self.resolve_anon_const(ct, AnonConstKind::ConstArg(IsRepeatExpr::Yes)); } ExprKind::ConstBlock(ref ct) => { - self.resolve_anon_const(ct, AnonConstKind::InlineConst); + self.with_move_expr_scope(None, |this| { + this.resolve_anon_const(ct, AnonConstKind::InlineConst); + }); } ExprKind::Index(ref elem, ref idx, _) => { self.resolve_expr(elem, Some(expr)); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 6182f64f63efd..7a7078deaadc4 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -262,7 +262,10 @@ enum ResolutionError<'ra> { /// Error E0416: identifier is bound more than once in the same pattern. IdentifierBoundMoreThanOnceInSamePattern(Ident), /// Error E0426: use of undeclared label. - UndeclaredLabel { name: Symbol, suggestion: Option }, + UndeclaredLabel { + name: Symbol, + suggestion: Option, + }, /// Error E0433: failed to resolve. FailedToResolve { segment: Symbol, @@ -273,7 +276,9 @@ enum ResolutionError<'ra> { message: String, }, /// Error E0434: can't capture dynamic environment in a fn item. - CannotCaptureDynamicEnvironmentInFnItem { suggest_closure: bool }, + CannotCaptureDynamicEnvironmentInFnItem { + suggest_closure: bool, + }, /// Error E0435: attempt to use a non-constant value in a constant. AttemptToUseNonConstantValueInConstant { ident: Ident, @@ -296,7 +301,9 @@ enum ResolutionError<'ra> { // FIXME(generic_const_parameter_types): This should give custom output specifying it's only // problematic to use *forward declared* parameters when the feature is enabled. /// ERROR E0770: the type of const parameters must not depend on other generic parameters. - ParamInTyOfConstParam { name: Symbol }, + ParamInTyOfConstParam { + name: Symbol, + }, /// cannot use self in const param SelfInConstParam, /// generic parameters must not be used inside const evaluations. @@ -310,11 +317,18 @@ enum ResolutionError<'ra> { /// generic parameters must not be used inside enum discriminants. /// /// This error is emitted even with `generic_const_exprs`. - ParamInEnumDiscriminant { name: Symbol, param_kind: ParamKindInEnumDiscriminant }, + ParamInEnumDiscriminant { + name: Symbol, + param_kind: ParamKindInEnumDiscriminant, + }, /// Error E0735: generic parameters with a default cannot use `Self` ForwardDeclaredSelf(ForwardGenericParamBanReason), /// Error E0767: use of unreachable label - UnreachableLabel { name: Symbol, definition_span: Span, suggestion: Option }, + UnreachableLabel { + name: Symbol, + definition_span: Span, + suggestion: Option, + }, /// Error E0323, E0324, E0325: mismatch between trait item and impl item. TraitImplMismatch { name: Ident, @@ -339,6 +353,11 @@ enum ResolutionError<'ra> { LowercaseSelf, /// A never pattern has a binding. BindingInNeverPattern, + CannotUseInnerLocalInMoveExpr { + move_expr_span: Span, + ident: Ident, + scope_span: Span, + }, } #[derive(Debug)] From 6ecca07523d750fd445cc5d54f7222bee3bb0d05 Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 14 Oct 2026 18:45:28 +0200 Subject: [PATCH 15/17] Use OS `join` operation to have the expected path separator --- .../remap-path-prefix-doctest-panic/rmake.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/run-make/remap-path-prefix-doctest-panic/rmake.rs b/tests/run-make/remap-path-prefix-doctest-panic/rmake.rs index 383955a270ab3..31088dc5fe7a3 100644 --- a/tests/run-make/remap-path-prefix-doctest-panic/rmake.rs +++ b/tests/run-make/remap-path-prefix-doctest-panic/rmake.rs @@ -6,6 +6,8 @@ //@ ignore-cross-compile +use std::path::Path; + use run_make_support::assertion_helpers::{assert_contains, assert_not_contains}; use run_make_support::rustdoc; @@ -39,16 +41,19 @@ fn panic_file(scope: Option<&str>) -> String { } fn main() { + let expected_remapped = Path::new("/REMAPPED").join("doctest.rs").to_string_lossy().to_string(); + let expected_unremapped = Path::new("src").join("doctest.rs").to_string_lossy().to_string(); + // Check that with debuginfo (and related) we get the remapped path for scope in [Some("debuginfo"), Some("object"), Some("all"), None] { let stdout = panic_file(scope); - assert_contains(&stdout, "/REMAPPED/doctest.rs"); - assert_not_contains(&stdout, "src/doctest.rs"); + assert_contains(&stdout, &expected_remapped); + assert_not_contains(&stdout, &expected_unremapped); } for scope in ["diagnostics", "documentation", "macro"] { let stdout = panic_file(Some(scope)); - assert_contains(&stdout, "src/doctest.rs"); - assert_not_contains(&stdout, "/REMAPPED/doctest.rs"); + assert_contains(&stdout, &expected_unremapped); + assert_not_contains(&stdout, &expected_remapped); } } From dd49010532dc6e671cc6e2c054e70a92b0e8cdf5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 17 Sep 2026 14:22:49 -0700 Subject: [PATCH 16/17] Test panics and wasm32-wasip3 in CI, not wasip1 This commit migrates the `test-various` job in CI from testing the `wasm32-wasip1` target to testing the `wasm32-wasip3` target. While doing so this additionally runs tests with `-Cpanic=unwind` instead of the historical `-Cpanic=abort` to expand the scope of tests slightly and run `alloc` tests, for example. The purpose of this change is to start migrating towards a wasm testing target which has a more fully-featured `std`, e.g. panics & threads, to run more tests in CI and make it less of a too-different target. This change was originally proposed at rust-lang/compiler-team/1039 which contains some more background details as well. The major notable change in test coverage here is running tests with `-Cpanic=unwind`. WebAssembly targets by default still use `-Cpanic=abort` and this means that large swaths of the test suite do not execute because they require panicking/unwinding in one way or another. CI here is configuring wasm targets to specifically enable unwinding, but only for the testing build. To achieve this a new `bootstrap.toml` option is added which enables configuring the default `panic_strategy` for wasm targets -- an option only intended to be used in testing. Most of this change is adjusting tests for this new testing infrastructure, some old Wasmtime-related TODOs are cleaned up as well with an updated build. Notably the `only-wasm32-wasip1` directive for compiletest is removed and all preexisting tests are migrated to run for any wasm target. The `run-make` suite in particular is updated to handle when the wasm target outputs components and the various ABI details of the wasip3 target. This change notably does not yet start to run `std`'s test suite for the `wasm32-wasip3` target. Too much of that test suite requires threads to be useful to run at this time, and as such running those tests is delayed until a later time when cooperative threading is enabled. Closes 163451 --- bootstrap.example.toml | 6 +++ compiler/rustc_target/src/spec/base/wasm.rs | 23 +++++--- .../src/collections/linked_list/tests.rs | 1 + library/alloc/src/rcs/arc.rs | 8 +-- library/alloctests/tests/sync.rs | 2 + library/test/src/lib.rs | 5 +- src/bootstrap/src/core/build_steps/compile.rs | 6 +++ src/bootstrap/src/core/config/config.rs | 4 ++ src/bootstrap/src/core/config/toml/rust.rs | 2 + src/bootstrap/src/core/session.rs | 21 +++----- .../host-x86_64/dist-various-2/Dockerfile | 2 +- .../host-x86_64/test-pr-check-2/Dockerfile | 6 +-- .../host-x86_64/test-various/Dockerfile | 14 ++--- .../src/directives/directive_names.rs | 1 - src/tools/run-make-support/Cargo.toml | 2 +- src/tools/run-make-support/src/lib.rs | 1 + src/tools/run-make-support/src/wasm.rs | 52 +++++++++++++++++++ tests/assembly-llvm/wasm_exceptions.rs | 2 - tests/assembly-llvm/wasm_legacy_eh.rs | 2 - tests/codegen-llvm/double_panic_wasm.rs | 2 +- tests/codegen-llvm/personality_lifetimes.rs | 1 + tests/codegen-llvm/wasm_custom_section.rs | 6 +-- tests/codegen-llvm/wasm_exceptions.rs | 1 + .../c-link-to-rust-va-list-fn/rmake.rs | 19 ++++--- tests/run-make/complex-abi/rmake.rs | 8 ++- tests/run-make/wasm-custom-section/rmake.rs | 9 ++-- .../wasm-custom-sections-opt/rmake.rs | 7 ++- .../run-make/wasm-export-all-symbols/rmake.rs | 27 +++++----- tests/run-make/wasm-import-module/rmake.rs | 6 +-- tests/run-make/wasm-panic-small/rmake.rs | 15 +++--- tests/run-make/wasm-spurious-import/main.rs | 9 ++++ tests/run-make/wasm-spurious-import/rmake.rs | 8 ++- .../wasm-stringify-ints-small/rmake.rs | 17 +++--- .../wasm-symbols-different-module/rmake.rs | 9 +++- .../run-make/wasm-symbols-not-exported/bar.rs | 11 +++- .../wasm-symbols-not-exported/rmake.rs | 16 +++--- .../run-make/wasm-symbols-not-imported/foo.rs | 9 ++++ .../wasm-symbols-not-imported/rmake.rs | 19 ++++--- tests/ui/backtrace/dylib-dep.rs | 2 +- tests/ui/backtrace/line-tables-only.rs | 2 +- ...internal-unstable-asm-experimental-arch.rs | 2 +- .../short-ice-remove-middle-frames-2.rs | 1 + ...hort-ice-remove-middle-frames-2.run.stderr | 2 +- .../panics/short-ice-remove-middle-frames.rs | 1 + .../short-ice-remove-middle-frames.run.stderr | 2 +- tests/ui/range/issue-54505-no-std.rs | 2 +- .../target-feature/implicit-features-cli.rs | 2 +- tests/ui/target-feature/implicit-features.rs | 2 +- tests/ui/target-feature/wasm-disable-simd.rs | 2 +- tests/ui/target-feature/wasm-relaxed-simd.rs | 2 +- ...sm-link-name-in-foreign-crate-respected.rs | 2 +- 51 files changed, 255 insertions(+), 128 deletions(-) create mode 100644 src/tools/run-make-support/src/wasm.rs diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 34ff4eb623917..846ae9ca314de 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -956,6 +956,12 @@ # want to enable this right now. #rust.wasm-proc-macros = false +# Enables wasm targets to be built with `-Cpanic=unwind` by default instead of +# the current `-Cpanic=abort`. This is currently used in testing the Rust +# compiler itself and is only intended for local development/testing at this +# time. +#rust.wasm-panic-unwind-default = false + # ============================================================================= # Distribution options # diff --git a/compiler/rustc_target/src/spec/base/wasm.rs b/compiler/rustc_target/src/spec/base/wasm.rs index 1596100e629d7..dd5bcff87cde1 100644 --- a/compiler/rustc_target/src/spec/base/wasm.rs +++ b/compiler/rustc_target/src/spec/base/wasm.rs @@ -60,12 +60,23 @@ pub(crate) fn options() -> TargetOptions { max_atomic_width: Some(64), - // Unwinding doesn't work right now, so the whole target unconditionally - // defaults to panic=abort. Note that this is guaranteed to change in - // the future once unwinding is implemented. Don't rely on this as we're - // basically guaranteed to change it once WebAssembly supports - // exceptions. - panic_strategy: PanicStrategy::Abort, + // WebAssembly targets all started out originally as `-Cpanic=abort` + // since that was all that worked. Since then, however, the WebAssembly + // exception-handling proposal has been finalized/implemented and is now + // supported in both LLVM & rustc & libstd & co. The default still + // remains at `-Cpanic=abort` for now for historical compatibility, + // but `bootstrap.toml` can be used to configure a local build to + // use `-Cpanic=unwind` instead. This is used in CI, for example, to + // test wasm targets with `-Cpanic=unwind`. + // + // In the future this might change where preexisting targets migrate, + // but that's not going to be as easy as just changing this, so for now + // this is just a local build configuration option. + panic_strategy: if option_env!("CFG_WASM_PANIC_UNWIND_DEFAULT") == Some("1") { + PanicStrategy::Unwind + } else { + PanicStrategy::Abort + }, // Wasm doesn't have atomics yet, so tell LLVM that we're in a single // threaded model which will legalize atomics to normal operations. diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index a51af6345e205..1fe63070f1a15 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -304,6 +304,7 @@ fn test_clone_from() { #[test] #[cfg_attr(target_os = "emscripten", ignore)] +#[cfg_attr(target_os = "wasi", ignore)] fn test_send() { let n = list_from(&[1, 2, 3]); thread::spawn(move || { diff --git a/library/alloc/src/rcs/arc.rs b/library/alloc/src/rcs/arc.rs index 8ae3c094b2910..1a8364e08d76c 100644 --- a/library/alloc/src/rcs/arc.rs +++ b/library/alloc/src/rcs/arc.rs @@ -213,7 +213,7 @@ macro_rules! acquire { /// /// Sharing some immutable data between threads: /// -/// ``` +/// ```ignore-wasm /// use std::sync::Arc; /// use std::thread; /// @@ -232,7 +232,7 @@ macro_rules! acquire { /// /// [`AtomicUsize`]: core::sync::atomic::AtomicUsize "sync::atomic::AtomicUsize" /// -/// ``` +/// ```ignore-wasm /// use std::sync::Arc; /// use std::sync::atomic::{AtomicUsize, Ordering}; /// use std::thread; @@ -1091,7 +1091,7 @@ impl Arc { /// # Examples /// /// Minimal example demonstrating the guarantee that `Arc::into_inner` gives. - /// ``` + /// ```ignore-wasm /// use std::sync::Arc; /// /// let x = Arc::new(3); @@ -1114,7 +1114,7 @@ impl Arc { /// ``` /// /// A more practical example demonstrating the need for `Arc::into_inner`: - /// ``` + /// ```ignore-wasm /// use std::sync::Arc; /// /// // Definition of a simple singly linked list using `Arc`: diff --git a/library/alloctests/tests/sync.rs b/library/alloctests/tests/sync.rs index 5742855768c70..05f90a1a78cce 100644 --- a/library/alloctests/tests/sync.rs +++ b/library/alloctests/tests/sync.rs @@ -58,6 +58,7 @@ impl Drop for AllocCanary<'_> { #[test] #[cfg_attr(target_os = "emscripten", ignore)] +#[cfg_attr(target_os = "wasi", ignore)] fn manually_share_arc() { let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let arc_v = Arc::new(v); @@ -454,6 +455,7 @@ fn test_ptr_eq() { #[test] #[cfg_attr(target_os = "emscripten", ignore)] +#[cfg_attr(target_os = "wasi", ignore)] fn test_weak_count_locked() { let mut a = Arc::new(atomic::AtomicBool::new(false)); let a2 = a.clone(); diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 7eb632ba69882..e385932bda802 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -616,10 +616,11 @@ pub fn run_test( ) -> Option> { let TestDescAndFn { desc, testfn } = test; - // Emscripten can catch panics but other wasm targets cannot + // Emscripten can catch panics, and test for `panic=unwind` on other wasm + // targets to see if they can catch panics. let ignore_because_no_process_support = desc.should_panic != ShouldPanic::No && (cfg!(target_family = "wasm") || cfg!(target_os = "zkvm")) - && !cfg!(target_os = "emscripten"); + && !(cfg!(target_os = "emscripten") || cfg!(panic = "unwind")); if force_ignore || desc.ignore || ignore_because_no_process_support { let message = CompletedTest::new(id, desc, TrIgnored, None, Vec::new()); diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 3aded58123525..47d7056ad84f7 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1421,6 +1421,12 @@ fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelec cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "14"); } } + + // Plumb the `rust.wasm-panic-unwind-default` configuration option into the + // `rustc_target` crate. + if builder.config.wasm_panic_unwind_default { + cargo.env("CFG_WASM_PANIC_UNWIND_DEFAULT", "1"); + } } /// Pass down configuration from the LLVM build into the build of diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 1e58d86024874..3d03cc547ab75 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -348,6 +348,8 @@ pub(crate) struct Config { pub exec_ctx: ExecutionContext, pub wasm_proc_macros: bool, + + pub wasm_panic_unwind_default: bool, } impl Config { @@ -624,6 +626,7 @@ impl Config { rustflags: rust_rustflags, stdlib_semver_baseline: rust_stdlib_semver_baseline, wasm_proc_macros, + wasm_panic_unwind_default, } = toml_rust.unwrap_or_default(); let Llvm { @@ -1612,6 +1615,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to .unwrap_or(rust_debug == Some(true)), vendor, verbose_tests, + wasm_panic_unwind_default: wasm_panic_unwind_default.unwrap_or(false), wasm_proc_macros: wasm_proc_macros.unwrap_or(false), windows_rc: build_windows_rc.map(PathBuf::from), yarn: build_yarn.map(PathBuf::from), diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index 0c88a3d8d0172..7360d8553fb55 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -81,6 +81,7 @@ define_config! { parallel_frontend_threads: Option = "parallel-frontend-threads", stdlib_semver_baseline: Option = "stdlib-semver-baseline", wasm_proc_macros: Option = "wasm-proc-macros", + wasm_panic_unwind_default: Option = "wasm-panic-unwind-default", } } @@ -400,6 +401,7 @@ pub fn check_incompatible_options_for_ci_rustc( rustflags: _, stdlib_semver_baseline: _, wasm_proc_macros: _, + wasm_panic_unwind_default: _, } = ci_rust_config; // There are two kinds of checks for CI rustc incompatible options: diff --git a/src/bootstrap/src/core/session.rs b/src/bootstrap/src/core/session.rs index 1808f1b8855bc..df6c8b3958736 100644 --- a/src/bootstrap/src/core/session.rs +++ b/src/bootstrap/src/core/session.rs @@ -1197,7 +1197,7 @@ impl Session { } if target.starts_with("wasm") && target.contains("wasi") { - self.default_wasi_runner(target) + self.default_wasi_runner() } else { None } @@ -1206,7 +1206,7 @@ impl Session { /// When a `runner` configuration is not provided and a WASI-looking target /// is being tested this is consulted to prove the environment to see if /// there's a runtime already lying around that seems reasonable to use. - fn default_wasi_runner(&self, target: TargetSelection) -> Option { + fn default_wasi_runner(&self) -> Option { let mut finder = crate::core::sanity::Finder::new(); // Look for Wasmtime, and for its default options be sure to disable @@ -1215,19 +1215,10 @@ impl Session { if let Some(path) = finder.maybe_have("wasmtime") && let Ok(mut path) = path.into_os_string().into_string() { - path.push_str(" run -Wexceptions -C cache=n --dir ."); - // Make sure that tests have access to RUSTC_BOOTSTRAP. This (for example) is - // required for libtest to work on beta/stable channels. - // - // NB: with Wasmtime 20 this can change to `-S inherit-env` to - // inherit the entire environment rather than just this single - // environment variable. - path.push_str(" --env RUSTC_BOOTSTRAP"); - - if target.contains("wasip2") { - path.push_str(" --wasi inherit-network --wasi allow-ip-name-lookup"); - } - + path.push_str( + " run -C cache=n --dir . \ + --wasi inherit-env,inherit-network,allow-ip-name-lookup", + ); return Some(path); } diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index d753d16aff66c..6be97366544cf 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -79,7 +79,7 @@ RUN /tmp/build-fuchsia-toolchain.sh COPY host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh /tmp/ RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sdk-34.0-x86_64-linux.tar.gz | \ +RUN curl -L https://ci-mirrors.rust-lang.org/rustc/wasi-sdk-34.0-x86_64-linux.tar.gz | \ tar -xz ENV WASI_SDK_PATH=/tmp/wasi-sdk-34.0-x86_64-linux diff --git a/src/ci/docker/host-x86_64/test-pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/test-pr-check-2/Dockerfile index 0a9b6f0fda91c..ac690d0bbdc08 100644 --- a/src/ci/docker/host-x86_64/test-pr-check-2/Dockerfile +++ b/src/ci/docker/host-x86_64/test-pr-check-2/Dockerfile @@ -21,7 +21,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ mingw-w64 \ && rm -rf /var/lib/apt/lists/* -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sdk-34.0-x86_64-linux.tar.gz | \ +RUN curl -L https://ci-mirrors.rust-lang.org/rustc/wasi-sdk-34.0-x86_64-linux.tar.gz | \ tar -xz ENV WASI_SDK_PATH=/wasi-sdk-34.0-x86_64-linux @@ -34,9 +34,9 @@ ENV SCRIPT="python3 ../x.py check && \ python3 ../x.py clippy ci --stage 2 && \ python3 ../x.py test --stage 1 core alloc std test proc_macro && \ # Elsewhere, we run all tests for the host. A number of codegen tests are sensitive to the target pointer - # width, for example because they mention a usize. wasm32-wasip1 in test-various, so using it here can't make + # width, for example because they mention a usize. wasm32-wasip3 in test-various, so using it here can't make # PR CI more strict than full CI. - python3 ../x.py test --stage 1 tests/codegen-llvm --target wasm32-wasip1 && \ + python3 ../x.py test --stage 1 tests/codegen-llvm --target wasm32-wasip3 && \ python3 ../x.py test --stage 1 src/tools/compiletest && \ python3 ../x.py doc bootstrap --stage 1 && \ # Build both public and internal documentation. diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 6034f3d1d883c..3136e38da5a43 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -43,12 +43,13 @@ WORKDIR / COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sdk-34.0-x86_64-linux.tar.gz | \ +RUN curl -L https://ci-mirrors.rust-lang.org/rustc/wasi-sdk-34.0-x86_64-linux.tar.gz | \ tar -xz ENV WASI_SDK_PATH=/wasi-sdk-34.0-x86_64-linux ENV RUST_CONFIGURE_ARGS="--musl-root-x86_64=/usr/local/x86_64-linux-musl \ - --set rust.lld" + --set rust.lld \ + --set rust.wasm-panic-unwind-default" # Some run-make tests have assertions about code size, and enabling debug # assertions in libstd causes the binary to be much bigger than it would @@ -57,11 +58,11 @@ ENV RUST_CONFIGURE_ARGS="--musl-root-x86_64=/usr/local/x86_64-linux-musl \ ENV NO_DEBUG_ASSERTIONS=1 ENV NO_OVERFLOW_CHECKS=1 -RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v44.0.1/wasmtime-v44.0.1-x86_64-linux.tar.xz | \ +RUN curl -L https://ci-mirrors.rust-lang.org/rustc/wasmtime-v48.0.2-x86_64-linux.tar.xz | \ tar -xJ -ENV PATH="$PATH:/wasmtime-v44.0.1-x86_64-linux" +ENV PATH="$PATH:/wasmtime-v48.0.2-x86_64-linux" -ENV WASM_WASIP_TARGET=wasm32-wasip1 +ENV WASM_WASIP_TARGET=wasm32-wasip3 ENV WASM_WASIP_SCRIPT="python3 /checkout/x.py --stage 2 test --host= --target $WASM_WASIP_TARGET \ tests/run-make \ tests/run-make-cargo \ @@ -70,7 +71,8 @@ ENV WASM_WASIP_SCRIPT="python3 /checkout/x.py --stage 2 test --host= --target $W tests/codegen-units \ tests/codegen-llvm \ tests/assembly-llvm \ - library/core" + library/core \ + library/alloc" ENV NVPTX_TARGETS=nvptx64-nvidia-cuda ENV NVPTX_SCRIPT="python3 /checkout/x.py --stage 2 test --host= --target $NVPTX_TARGETS \ diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 732f4ab275dd7..520a5468e45f0 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -273,7 +273,6 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-wasm32", "only-wasm32-unknown-emscripten", "only-wasm32-unknown-unknown", - "only-wasm32-wasip1", "only-watchos", "only-windows", "only-windows-gnu", diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 0e1310862589d..2f503aaec5f92 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -18,7 +18,7 @@ regex = "1.11" serde_json = "1.0" similar = "2.7" tempfile = "3" -wasmparser = { version = "0.247", default-features = false, features = ["std", "features", "validate"] } +wasmparser = { version = "0.247", default-features = false, features = ["std", "features", "validate", "component-model"] } # tidy-alphabetical-end # Shared with bootstrap and compiletest diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index b9717bda1e2c7..f800dd61e47d6 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -22,6 +22,7 @@ pub mod scoped_run; pub mod string; pub mod symbols; pub mod targets; +pub mod wasm; // Internally we call our fs-related support module as `fs`, but re-export its content as `rfs` // to tests to avoid colliding with commonly used `use std::fs;`. diff --git a/src/tools/run-make-support/src/wasm.rs b/src/tools/run-make-support/src/wasm.rs new file mode 100644 index 0000000000000..16395ab6c90b1 --- /dev/null +++ b/src/tools/run-make-support/src/wasm.rs @@ -0,0 +1,52 @@ +//! Wasm-specific helpers for various tests + +use wasmparser::{Parser, Payload}; + +use crate::{Rustc, target}; + +/// Returns whether the current compilation target will emit a WebAssembly +/// component. +pub fn target_emits_components() -> bool { + let target = target(); + if target.contains("wasi") { + return !target.contains("wasip1"); + } + if target.contains("component") { + return true; + } + return false; +} + +/// Helper methods for the `Rustc` type. +impl Rustc { + /// Helper to emit a core module for this wasm target, skipping the + /// component output part of compilation. + pub fn emit_wasm_core_module(&mut self) -> &mut Self { + if target_emits_components() { + self.arg("-Clink-arg=--skip-wit-component"); + } + self + } +} + +/// Returns the size, in bytes, of the wasm module in `bytes`. +/// +/// This excludes custom sections to avoid counting easily strippable sections +/// such as debuginfo. +pub fn wasm_binary_size(bytes: &[u8]) -> usize { + let mut size = 0; + for payload in Parser::new(0).parse_all(bytes) { + let payload = payload.unwrap(); + match payload { + // Don't count the size of custom sections as they're easily + // stripped, and don't double-count modules since this'll recurse + // into module. + Payload::CustomSection { .. } | Payload::ModuleSection { .. } => continue, + _ => {} + } + if let Some((_, range)) = payload.as_section() { + size += range.len(); + } + } + size +} diff --git a/tests/assembly-llvm/wasm_exceptions.rs b/tests/assembly-llvm/wasm_exceptions.rs index 910e508b86f65..89ff390f3c082 100644 --- a/tests/assembly-llvm/wasm_exceptions.rs +++ b/tests/assembly-llvm/wasm_exceptions.rs @@ -32,7 +32,6 @@ pub fn test_cleanup() { may_panic(); } - // CHECK-NOT: call // CHECK: try_table (catch_all_ref 0) // CHECK: call may_panic // CHECK: end_try_table @@ -56,7 +55,6 @@ pub fn test_rtry() { ); } - // CHECK-NOT: call // CHECK: try_table (catch __cpp_exception 0) // CHECK: call may_panic // CHECK: end_try_table diff --git a/tests/assembly-llvm/wasm_legacy_eh.rs b/tests/assembly-llvm/wasm_legacy_eh.rs index 89388bfec1d5f..fd5b84eee7c9b 100644 --- a/tests/assembly-llvm/wasm_legacy_eh.rs +++ b/tests/assembly-llvm/wasm_legacy_eh.rs @@ -42,7 +42,6 @@ pub fn test_cleanup() { // CHECK-NOT: try_table // CHECK-NOT: catch_all_ref // CHECK-NOT: throw_ref - // CHECK-NOT: call // CHECK: try // CHECK: call may_panic // CHECK: catch_all @@ -69,7 +68,6 @@ pub fn test_rtry() { // CHECK-NOT: try_table // CHECK-NOT: catch_all_ref // CHECK-NOT: throw_ref - // CHECK-NOT: call // CHECK: try // CHECK: call may_panic // CHECK: catch diff --git a/tests/codegen-llvm/double_panic_wasm.rs b/tests/codegen-llvm/double_panic_wasm.rs index 1eafe60503809..e69066b8ddece 100644 --- a/tests/codegen-llvm/double_panic_wasm.rs +++ b/tests/codegen-llvm/double_panic_wasm.rs @@ -21,7 +21,7 @@ impl Drop for PanicOnDrop { // CHECK-LABEL: @double_panic // CHECK: invoke void @may_panic() -// CHECK: invoke void @{{.+}}drop_in_place{{.+}} +// CHECK: invoke void @{{.+}}drop_glue{{.+}} // CHECK: unwind label %[[TERMINATE:.*]] // // CHECK: [[TERMINATE]]: diff --git a/tests/codegen-llvm/personality_lifetimes.rs b/tests/codegen-llvm/personality_lifetimes.rs index cd81db6395349..9d587961cbeef 100644 --- a/tests/codegen-llvm/personality_lifetimes.rs +++ b/tests/codegen-llvm/personality_lifetimes.rs @@ -1,4 +1,5 @@ //@ ignore-msvc +//@ ignore-wasm //@ needs-unwind //@ compile-flags: -Copt-level=3 -C no-prepopulate-passes diff --git a/tests/codegen-llvm/wasm_custom_section.rs b/tests/codegen-llvm/wasm_custom_section.rs index 7e111c6acc352..4e9bed0ec66d7 100644 --- a/tests/codegen-llvm/wasm_custom_section.rs +++ b/tests/codegen-llvm/wasm_custom_section.rs @@ -11,9 +11,9 @@ static FOO: [u8; 22] = *b"custom section content"; // Data segment in foo section -// CHECK: @FOO = dso_local constant [22 x i8] c"custom section content", section "foo" +// CHECK: @FOO = {{.*}}constant [22 x i8] c"custom section content", section "foo" // CHECK: @llvm.used = appending global [1 x ptr] [ptr @FOO], section "llvm.metadata" // Custom section -// CHECK: !wasm.custom_sections = !{!3} -// CHECK: !3 = !{!"foo", !"custom section content"} +// CHECK: !wasm.custom_sections = !{![[ID:[0-9]+]]} +// CHECK: ![[ID]] = !{!"foo", !"custom section content"} diff --git a/tests/codegen-llvm/wasm_exceptions.rs b/tests/codegen-llvm/wasm_exceptions.rs index b4c024e4ed0e6..e586f5b111253 100644 --- a/tests/codegen-llvm/wasm_exceptions.rs +++ b/tests/codegen-llvm/wasm_exceptions.rs @@ -1,5 +1,6 @@ //@ only-wasm32 //@ revisions: WASM WASMEXN +//@ [WASM] compile-flags: -C panic=abort //@ [WASMEXN] compile-flags: -C panic=unwind #![crate_type = "lib"] diff --git a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs index 420cf0e48e402..308733f0b4333 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs @@ -11,14 +11,21 @@ // FIXME: Once GCC backend is fixed, remove this `ignore-backends`. //@ ignore-backends: gcc -use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name}; +use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name, target}; fn main() { rustc().edition("2021").input("checkrust.rs").run(); - cc().input("test.c") - .input(static_lib_name("checkrust")) - .out_exe("test") - .args(extra_c_flags()) - .run(); + let mut cmd = cc(); + cmd.input("test.c").input(static_lib_name("checkrust")).out_exe("test").args(extra_c_flags()); + + // Wasm targets with `-Cpanic=unwind` are a bit nonstandard right now and + // require some extra flags. + if target().contains("wasm") + && !rustc().arg("--print=cfg").run().stdout_utf8().contains("abort") + { + cmd.arg("-lunwind").arg("-fwasm-exceptions").arg("-Wl,--export-if-defined=cabi_realloc"); + } + cmd.run(); + run("test"); } diff --git a/tests/run-make/complex-abi/rmake.rs b/tests/run-make/complex-abi/rmake.rs index 4836e72717513..175cd47e1300b 100644 --- a/tests/run-make/complex-abi/rmake.rs +++ b/tests/run-make/complex-abi/rmake.rs @@ -5,10 +5,14 @@ //@ ignore-pauthtest: (it requires non-trivial compilation of c sources, and only supports dynamic // linking, ignore the test). -use run_make_support::{bin_name, build_native_static_lib, env_var, run, rustc}; +use run_make_support::{bin_name, build_native_static_lib, env_var, run, rustc, target}; fn main() { build_native_static_lib("test"); - rustc().linker(&env_var("CC")).input("main.rs").run(); + let mut cmd = rustc(); + if !target().contains("wasm") { + cmd.linker(&env_var("CC")); + } + cmd.input("main.rs").run(); run(&bin_name("main")); } diff --git a/tests/run-make/wasm-custom-section/rmake.rs b/tests/run-make/wasm-custom-section/rmake.rs index ff4c29a9d3090..429b85e51e243 100644 --- a/tests/run-make/wasm-custom-section/rmake.rs +++ b/tests/run-make/wasm-custom-section/rmake.rs @@ -1,12 +1,12 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::collections::HashMap; use run_make_support::{rfs, rustc, wasmparser}; fn main() { - rustc().input("foo.rs").target("wasm32-wasip1").run(); - rustc().input("bar.rs").target("wasm32-wasip1").arg("-Clto").opt().run(); + rustc().input("foo.rs").run(); + rustc().input("bar.rs").arg("-Clto").opt().run(); let file = rfs::read("bar.wasm"); @@ -14,8 +14,7 @@ fn main() { for payload in wasmparser::Parser::new(0).parse_all(&file) { let payload = payload.unwrap(); if let wasmparser::Payload::CustomSection(s) = payload { - let prev = custom.insert(s.name(), s.data()); - assert!(prev.is_none()); + custom.insert(s.name(), s.data()); } } diff --git a/tests/run-make/wasm-custom-sections-opt/rmake.rs b/tests/run-make/wasm-custom-sections-opt/rmake.rs index 063a4f2257c80..c718ba0b7adbc 100644 --- a/tests/run-make/wasm-custom-sections-opt/rmake.rs +++ b/tests/run-make/wasm-custom-sections-opt/rmake.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::collections::HashMap; use std::path::Path; @@ -6,7 +6,7 @@ use std::path::Path; use run_make_support::{rfs, rustc, wasmparser}; fn main() { - rustc().input("foo.rs").target("wasm32-wasip1").opt().run(); + rustc().input("foo.rs").opt().run(); verify(Path::new("foo.wasm")); } @@ -19,8 +19,7 @@ fn verify(path: &Path) { for payload in wasmparser::Parser::new(0).parse_all(&file) { let payload = payload.unwrap(); if let wasmparser::Payload::CustomSection(s) = payload { - let prev = custom.insert(s.name(), s.data()); - assert!(prev.is_none()); + custom.insert(s.name(), s.data()); } } diff --git a/tests/run-make/wasm-export-all-symbols/rmake.rs b/tests/run-make/wasm-export-all-symbols/rmake.rs index 58abfb996461e..3916c81363f9d 100644 --- a/tests/run-make/wasm-export-all-symbols/rmake.rs +++ b/tests/run-make/wasm-export-all-symbols/rmake.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::collections::HashMap; use std::path::Path; @@ -15,9 +15,9 @@ fn main() { fn test(args: &[&str]) { eprintln!("running with {args:?}"); - rustc().input("bar.rs").target("wasm32-wasip1").args(args).run(); - rustc().input("foo.rs").target("wasm32-wasip1").args(args).run(); - rustc().input("main.rs").target("wasm32-wasip1").args(args).run(); + rustc().input("bar.rs").args(args).run(); + rustc().input("foo.rs").args(args).emit_wasm_core_module().run(); + rustc().input("main.rs").args(args).emit_wasm_core_module().run(); verify_exports( Path::new("foo.wasm"), @@ -25,13 +25,7 @@ fn test(args: &[&str]) { ); verify_exports( Path::new("main.wasm"), - &[ - ("foo", Func), - ("FOO", Global), - ("_start", Func), - ("__main_void", Func), - ("memory", Memory), - ], + &[("foo", Func), ("FOO", Global), ("__main_void", Func), ("memory", Memory)], ); } @@ -44,7 +38,16 @@ fn verify_exports(path: &Path, exports: &[(&str, wasmparser::ExternalKind)]) { if let wasmparser::Payload::ExportSection(s) = payload { for export in s { let export = export.unwrap(); - wasm_exports.insert(export.name, export.kind); + match export.name { + "__wasm_task_hook" + | "cabi_realloc" + | "__indirect_function_table" + | "_start" => {} + name if name.starts_with("wasi:") => {} + other => { + wasm_exports.insert(other, export.kind); + } + } } } } diff --git a/tests/run-make/wasm-import-module/rmake.rs b/tests/run-make/wasm-import-module/rmake.rs index 77614ce1ceb86..8cafd0ad20d25 100644 --- a/tests/run-make/wasm-import-module/rmake.rs +++ b/tests/run-make/wasm-import-module/rmake.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::collections::HashMap; @@ -6,8 +6,8 @@ use run_make_support::{rfs, rustc, wasmparser}; use wasmparser::TypeRef::Func; fn main() { - rustc().input("foo.rs").target("wasm32-wasip1").run(); - rustc().input("bar.rs").target("wasm32-wasip1").arg("-Clto").opt().run(); + rustc().input("foo.rs").run(); + rustc().input("bar.rs").emit_wasm_core_module().arg("-Clto").opt().run(); let file = rfs::read("bar.wasm"); diff --git a/tests/run-make/wasm-panic-small/rmake.rs b/tests/run-make/wasm-panic-small/rmake.rs index 64c17571437c5..946d6ca625b72 100644 --- a/tests/run-make/wasm-panic-small/rmake.rs +++ b/tests/run-make/wasm-panic-small/rmake.rs @@ -1,7 +1,7 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 #![deny(warnings)] -use run_make_support::{rfs, rustc}; +use run_make_support::{rfs, rustc, wasm}; fn main() { test("a"); @@ -15,18 +15,15 @@ fn test(cfg: &str) { rustc() .input("foo.rs") - .target("wasm32-wasip1") .arg("-Clto") + .arg("-Cpanic=abort") .arg("-Cstrip=debuginfo") .opt() .cfg(cfg) .run(); let bytes = rfs::read("foo.wasm"); - println!("{}", bytes.len()); - assert!( - bytes.len() < 50_000, - "wasm binary was larger than expected. size in bytes: {}", - bytes.len() - ); + let size = wasm::wasm_binary_size(&bytes); + println!("{size}"); + assert!(size < 60_000, "bytes len was: {size}"); } diff --git a/tests/run-make/wasm-spurious-import/main.rs b/tests/run-make/wasm-spurious-import/main.rs index fcbead5e28bd2..a83f3e2326e94 100644 --- a/tests/run-make/wasm-spurious-import/main.rs +++ b/tests/run-make/wasm-spurious-import/main.rs @@ -12,3 +12,12 @@ pub fn multer(a: i128, b: i128) -> i128 { // panic function in case of a bug. We verify that no imports exist in our verifier. a * b } + +#[cfg(target_env = "p3")] +#[no_mangle] +pub extern "C" fn __wasm_task_hook(_: u32) {} +#[cfg(target_env = "p3")] +#[no_mangle] +pub extern "C" fn cabi_realloc(_: *mut u8, _: usize, _: usize, _: usize) -> *mut u8 { + loop {} +} diff --git a/tests/run-make/wasm-spurious-import/rmake.rs b/tests/run-make/wasm-spurious-import/rmake.rs index 435c6219aa502..e6e8a4dfc4104 100644 --- a/tests/run-make/wasm-spurious-import/rmake.rs +++ b/tests/run-make/wasm-spurious-import/rmake.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::collections::HashMap; @@ -7,11 +7,11 @@ use run_make_support::{rfs, rustc, wasmparser}; fn main() { rustc() .input("main.rs") - .target("wasm32-wasip1") .arg("-Coverflow-checks") .arg("-Cpanic=abort") .arg("-Clto") .arg("-Copt-level=z") + .emit_wasm_core_module() .run(); let file = rfs::read("main.wasm"); @@ -22,6 +22,10 @@ fn main() { if let wasmparser::Payload::ImportSection(s) = payload { for i in s.into_imports() { let i = i.unwrap(); + // ignore intrinsics like `__wasm_{get,set}_stack_pointer` + if i.name.starts_with("__wasm_") { + continue; + } imports.entry(i.module).or_insert(Vec::new()).push((i.name, i.ty)); } } diff --git a/tests/run-make/wasm-stringify-ints-small/rmake.rs b/tests/run-make/wasm-stringify-ints-small/rmake.rs index c0448c59c037b..060fe578d5c2d 100644 --- a/tests/run-make/wasm-stringify-ints-small/rmake.rs +++ b/tests/run-make/wasm-stringify-ints-small/rmake.rs @@ -1,18 +1,13 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 #![deny(warnings)] -use run_make_support::{rfs, rustc}; +use run_make_support::{rfs, rustc, wasm}; fn main() { - rustc() - .input("foo.rs") - .target("wasm32-wasip1") - .arg("-Clto") - .arg("-Cstrip=debuginfo") - .opt() - .run(); + rustc().input("foo.rs").arg("-Clto").emit_wasm_core_module().opt().run(); let bytes = rfs::read("foo.wasm"); - println!("{}", bytes.len()); - assert!(bytes.len() < 50_000); + let size = wasm::wasm_binary_size(&bytes); + println!("{size}"); + assert!(size < 75_000); } diff --git a/tests/run-make/wasm-symbols-different-module/rmake.rs b/tests/run-make/wasm-symbols-different-module/rmake.rs index 0e7113c3df8f9..8c8b6f21b2014 100644 --- a/tests/run-make/wasm-symbols-different-module/rmake.rs +++ b/tests/run-make/wasm-symbols-different-module/rmake.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::collections::{HashMap, HashSet}; use std::path::Path; @@ -22,7 +22,7 @@ fn test_file(file: &str, expected_imports: &[(&str, &[&str])]) { fn test(file: &str, args: &[&str], expected_imports: &[(&str, &[&str])]) { println!("test {file:?} {args:?} for {expected_imports:?}"); - rustc().input(file).target("wasm32-wasip1").args(args).run(); + rustc().input(file).emit_wasm_core_module().args(args).run(); let file = rfs::read(Path::new(file).with_extension("wasm")); @@ -32,6 +32,11 @@ fn test(file: &str, args: &[&str], expected_imports: &[(&str, &[&str])]) { if let wasmparser::Payload::ImportSection(s) = payload { for i in s.into_imports() { let i = i.unwrap(); + // Ignore intrinsics like `__wasm_{get,set}_stack_pointer` as + // well as standard library dependencies + if i.name.starts_with("__wasm_") || i.module.starts_with("wasi:") { + continue; + } imports.entry(i.module).or_insert(HashSet::new()).insert(i.name); } } diff --git a/tests/run-make/wasm-symbols-not-exported/bar.rs b/tests/run-make/wasm-symbols-not-exported/bar.rs index dd4d14416e70e..79d0fb4bf0def 100644 --- a/tests/run-make/wasm-symbols-not-exported/bar.rs +++ b/tests/run-make/wasm-symbols-not-exported/bar.rs @@ -1,4 +1,4 @@ -#![feature(panic_handler, alloc_error_handler)] +#![feature(alloc_error_handler)] #![crate_type = "cdylib"] #![no_std] @@ -32,3 +32,12 @@ fn a(_: core::alloc::Layout) -> ! { fn b(_: &core::panic::PanicInfo) -> ! { loop {} } + +#[cfg(target_env = "p3")] +#[no_mangle] +pub extern "C" fn __wasm_task_hook(_: u32) {} +#[cfg(target_env = "p3")] +#[no_mangle] +pub extern "C" fn cabi_realloc(_: *mut u8, _: usize, _: usize, _: usize) -> *mut u8 { + loop {} +} diff --git a/tests/run-make/wasm-symbols-not-exported/rmake.rs b/tests/run-make/wasm-symbols-not-exported/rmake.rs index 62da63db26dbf..de8852439a00f 100644 --- a/tests/run-make/wasm-symbols-not-exported/rmake.rs +++ b/tests/run-make/wasm-symbols-not-exported/rmake.rs @@ -1,18 +1,18 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::path::Path; use run_make_support::{rfs, rustc, wasmparser}; fn main() { - rustc().input("foo.rs").target("wasm32-wasip1").run(); + rustc().input("foo.rs").emit_wasm_core_module().run(); verify_symbols(Path::new("foo.wasm")); - rustc().input("foo.rs").target("wasm32-wasip1").opt().run(); + rustc().input("foo.rs").emit_wasm_core_module().opt().run(); verify_symbols(Path::new("foo.wasm")); - rustc().input("bar.rs").target("wasm32-wasip1").run(); + rustc().input("bar.rs").emit_wasm_core_module().arg("-Cpanic=abort").run(); verify_symbols(Path::new("bar.wasm")); - rustc().input("bar.rs").target("wasm32-wasip1").opt().run(); + rustc().input("bar.rs").emit_wasm_core_module().arg("-Cpanic=abort").opt().run(); verify_symbols(Path::new("bar.wasm")); } @@ -28,7 +28,11 @@ fn verify_symbols(path: &Path) { if e.kind != wasmparser::ExternalKind::Func { continue; } - if e.name == "foo" || e.name == "_initialize" { + if e.name == "foo" + || e.name == "_initialize" + || e.name == "__wasm_task_hook" + || e.name == "cabi_realloc" + { continue; } panic!("unexpected export {e:?}"); diff --git a/tests/run-make/wasm-symbols-not-imported/foo.rs b/tests/run-make/wasm-symbols-not-imported/foo.rs index 6af8d50a2ace2..187d8434b8572 100644 --- a/tests/run-make/wasm-symbols-not-imported/foo.rs +++ b/tests/run-make/wasm-symbols-not-imported/foo.rs @@ -12,3 +12,12 @@ pub extern "C" fn foo() { fn panic(_info: &PanicInfo) -> ! { loop {} } + +#[cfg(target_env = "p3")] +#[no_mangle] +pub extern "C" fn __wasm_task_hook(_: u32) {} +#[cfg(target_env = "p3")] +#[no_mangle] +pub extern "C" fn cabi_realloc(_: *mut u8, _: usize, _: usize, _: usize) -> *mut u8 { + loop {} +} diff --git a/tests/run-make/wasm-symbols-not-imported/rmake.rs b/tests/run-make/wasm-symbols-not-imported/rmake.rs index fc22e31356aaf..e2e06ed31abf4 100644 --- a/tests/run-make/wasm-symbols-not-imported/rmake.rs +++ b/tests/run-make/wasm-symbols-not-imported/rmake.rs @@ -1,17 +1,17 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::path::Path; use run_make_support::{rfs, rustc, wasmparser}; fn main() { - rustc().input("foo.rs").target("wasm32-wasip1").run(); + rustc().input("foo.rs").emit_wasm_core_module().arg("-Cpanic=abort").run(); verify_symbols(Path::new("foo.wasm")); - rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").run(); + rustc().input("foo.rs").emit_wasm_core_module().arg("-Cpanic=abort").arg("-Clto").run(); verify_symbols(Path::new("foo.wasm")); - rustc().input("foo.rs").target("wasm32-wasip1").opt().run(); + rustc().input("foo.rs").emit_wasm_core_module().arg("-Cpanic=abort").opt().run(); verify_symbols(Path::new("foo.wasm")); - rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").opt().run(); + rustc().input("foo.rs").emit_wasm_core_module().arg("-Cpanic=abort").arg("-Clto").opt().run(); verify_symbols(Path::new("foo.wasm")); } @@ -21,8 +21,13 @@ fn verify_symbols(path: &Path) { for payload in wasmparser::Parser::new(0).parse_all(&file) { let payload = payload.unwrap(); - if let wasmparser::Payload::ImportSection(_) = payload { - panic!("import section found"); + if let wasmparser::Payload::ImportSection(i) = payload { + for import in i.into_imports() { + let import = import.unwrap(); + if !import.name.starts_with("__wasm_") { + panic!("unexpected import: {import:?}"); + } + } } } } diff --git a/tests/ui/backtrace/dylib-dep.rs b/tests/ui/backtrace/dylib-dep.rs index f3d0d2e6a76db..a51ce3d8d04cc 100644 --- a/tests/ui/backtrace/dylib-dep.rs +++ b/tests/ui/backtrace/dylib-dep.rs @@ -15,7 +15,7 @@ //@ needs-unwind //@ ignore-backends: gcc //@ compile-flags: -g -Copt-level=0 -Cstrip=none -Cforce-frame-pointers=yes -//@ ignore-emscripten Requires custom symbolization code +//@ ignore-wasm Requires custom symbolization code //@ aux-crate: dylib_dep_helper=dylib-dep-helper.rs //@ aux-crate: auxiliary=dylib-dep-helper-aux.rs //@ run-pass diff --git a/tests/ui/backtrace/line-tables-only.rs b/tests/ui/backtrace/line-tables-only.rs index 33d5774079d94..39577385e123e 100644 --- a/tests/ui/backtrace/line-tables-only.rs +++ b/tests/ui/backtrace/line-tables-only.rs @@ -11,7 +11,7 @@ //@ compile-flags: -Cstrip=none -Cdebuginfo=line-tables-only //@ ignore-android FIXME #17520 //@ ignore-fuchsia Backtraces not symbolized -//@ ignore-emscripten Requires custom symbolization code +//@ ignore-wasm Requires custom symbolization code //@ ignore-ios needs the `.dSYM` files to be moved to the device //@ ignore-tvos needs the `.dSYM` files to be moved to the device //@ ignore-watchos needs the `.dSYM` files to be moved to the device diff --git a/tests/ui/internal/internal-unstable-asm-experimental-arch.rs b/tests/ui/internal/internal-unstable-asm-experimental-arch.rs index 6d97779ff911d..a073b397d9bd6 100644 --- a/tests/ui/internal/internal-unstable-asm-experimental-arch.rs +++ b/tests/ui/internal/internal-unstable-asm-experimental-arch.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 //@ compile-flags: --crate-type=lib //@ build-pass //@ aux-build:internal_unstable.rs diff --git a/tests/ui/panics/short-ice-remove-middle-frames-2.rs b/tests/ui/panics/short-ice-remove-middle-frames-2.rs index 28556304be622..9761c9e7a514c 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames-2.rs +++ b/tests/ui/panics/short-ice-remove-middle-frames-2.rs @@ -9,6 +9,7 @@ //@ ignore-sgx Backtraces not symbolized //@ ignore-fuchsia Backtraces not symbolized //@ ignore-msvc the `__rust_{begin,end}_short_backtrace` symbols aren't reliable. +//@ ignore-wasm backtraces not symbolized // This is needed to avoid test output differences across std being built with v0 symbols vs legacy // symbols. //@ normalize-stderr: "begin_panic::<&str>" -> "begin_panic" diff --git a/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr b/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr index 1efd7dc59b591..31bd4662af041 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr +++ b/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr @@ -1,5 +1,5 @@ -thread 'main' ($TID) panicked at $DIR/short-ice-remove-middle-frames-2.rs:63:5: +thread 'main' ($TID) panicked at $DIR/short-ice-remove-middle-frames-2.rs:64:5: debug!!! stack backtrace: 0: std::panicking::begin_panic diff --git a/tests/ui/panics/short-ice-remove-middle-frames.rs b/tests/ui/panics/short-ice-remove-middle-frames.rs index 0468bffbe4446..5754414bdf48e 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames.rs +++ b/tests/ui/panics/short-ice-remove-middle-frames.rs @@ -9,6 +9,7 @@ //@ ignore-sgx Backtraces not symbolized //@ ignore-fuchsia Backtraces not symbolized //@ ignore-msvc the `__rust_{begin,end}_short_backtrace` symbols aren't reliable. +//@ ignore-wasm backtraces not symbolized // This is needed to avoid test output differences across std being built with v0 symbols vs legacy // symbols. diff --git a/tests/ui/panics/short-ice-remove-middle-frames.run.stderr b/tests/ui/panics/short-ice-remove-middle-frames.run.stderr index 6ad72bbb43ca2..5796347fea6fe 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames.run.stderr +++ b/tests/ui/panics/short-ice-remove-middle-frames.run.stderr @@ -1,5 +1,5 @@ -thread 'main' ($TID) panicked at $DIR/short-ice-remove-middle-frames.rs:59:5: +thread 'main' ($TID) panicked at $DIR/short-ice-remove-middle-frames.rs:60:5: debug!!! stack backtrace: 0: std::panicking::begin_panic diff --git a/tests/ui/range/issue-54505-no-std.rs b/tests/ui/range/issue-54505-no-std.rs index 6ddfc7e231464..bf5d9e64d0d60 100644 --- a/tests/ui/range/issue-54505-no-std.rs +++ b/tests/ui/range/issue-54505-no-std.rs @@ -9,7 +9,7 @@ use core::ops::RangeBounds; -#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] +#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten", target_os = "wasi"))] #[lang = "eh_personality"] extern "C" fn eh_personality() {} diff --git a/tests/ui/target-feature/implicit-features-cli.rs b/tests/ui/target-feature/implicit-features-cli.rs index 34e7c3d506659..488929b662ccb 100644 --- a/tests/ui/target-feature/implicit-features-cli.rs +++ b/tests/ui/target-feature/implicit-features-cli.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 //@ compile-flags: -Ctarget-feature=+relaxed-simd --crate-type=lib //@ build-pass diff --git a/tests/ui/target-feature/implicit-features.rs b/tests/ui/target-feature/implicit-features.rs index b9c48b0822d53..c5d8a4907a8db 100644 --- a/tests/ui/target-feature/implicit-features.rs +++ b/tests/ui/target-feature/implicit-features.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 //@ compile-flags: --crate-type=lib //@ build-pass diff --git a/tests/ui/target-feature/wasm-disable-simd.rs b/tests/ui/target-feature/wasm-disable-simd.rs index 998c867482372..6115a6acb30aa 100644 --- a/tests/ui/target-feature/wasm-disable-simd.rs +++ b/tests/ui/target-feature/wasm-disable-simd.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 //@ compile-flags: -Ctarget-feature=-simd128 --crate-type=lib //@ build-pass diff --git a/tests/ui/target-feature/wasm-relaxed-simd.rs b/tests/ui/target-feature/wasm-relaxed-simd.rs index 34e7c3d506659..488929b662ccb 100644 --- a/tests/ui/target-feature/wasm-relaxed-simd.rs +++ b/tests/ui/target-feature/wasm-relaxed-simd.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 //@ compile-flags: -Ctarget-feature=+relaxed-simd --crate-type=lib //@ build-pass diff --git a/tests/ui/wasm/wasm-link-name-in-foreign-crate-respected.rs b/tests/ui/wasm/wasm-link-name-in-foreign-crate-respected.rs index e2ceeb8ae13e2..a39a9c6ada399 100644 --- a/tests/ui/wasm/wasm-link-name-in-foreign-crate-respected.rs +++ b/tests/ui/wasm/wasm-link-name-in-foreign-crate-respected.rs @@ -1,6 +1,6 @@ //@ only-wasm32 //@ aux-build:link-name-in-foreign-crate.rs -//@ compile-flags: --crate-type cdylib +//@ compile-flags: --crate-type cdylib -C link-arg=--skip-wit-component //@ build-pass //@ no-prefer-dynamic From 7fec32e649049621df4fd758c218e5800f021180 Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 14 Oct 2026 23:17:01 +0200 Subject: [PATCH 17/17] Ignore the remap rustdoc-json tests on windows --- tests/rustdoc-json/remap-path-prefix/doc.rs | 1 + tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/rustdoc-json/remap-path-prefix/doc.rs b/tests/rustdoc-json/remap-path-prefix/doc.rs index 11fa449983754..aa3d13255753e 100644 --- a/tests/rustdoc-json/remap-path-prefix/doc.rs +++ b/tests/rustdoc-json/remap-path-prefix/doc.rs @@ -1,3 +1,4 @@ +//@ ignore-windows //@ compile-flags: --remap-path-prefix={{src-base}}=/remapped //@ compile-flags: --remap-path-scope=documentation -Zunstable-options diff --git a/tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs b/tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs index 1d0a1fa65fad0..b3c2001ac5fb2 100644 --- a/tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs +++ b/tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs @@ -1,3 +1,4 @@ +//@ ignore-windows //@ compile-flags: --remap-path-prefix={{src-base}}=/remapped //@ is "$.index[?(@.name=='StrLike')].span.filename" \"/remapped/remap-path-prefix/remap-path-prefix.rs\"