diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 675bee738a672..e30e3c1c0283b 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -43,9 +43,21 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { let ExprKind::MethodCall(call, elements, _) = &expr.kind else { return }; + + let typeck_results = cx.typeck_results(); + + let receiver = &elements[0]; + let receiver_ty = typeck_results.expr_ty(receiver); + let expr_ty = typeck_results.expr_ty_adjusted(expr); + if receiver_ty != expr_ty { + // This lint will only trigger if the receiver type and resulting expression \ + // type are the same, implying that the method call is unnecessary. + return; + } + // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow` // traits and ignore any other method call. - let (trait_id, did) = match cx.typeck_results().type_dependent_def(expr.hir_id) { + let (trait_id, did) = match typeck_results.type_dependent_def(expr.hir_id) { // Verify we are dealing with a method/associated function. Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) { // Check that we're dealing with a trait method for one of the traits we care about. @@ -61,16 +73,22 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { }, _ => return, }; - let substs = cx.typeck_results().node_substs(expr.hir_id); + let substs = typeck_results.node_substs(expr.hir_id); if substs.needs_subst() { // We can't resolve on types that require monomorphization, so we don't handle them if // we need to perform substitution. return; } + + if cx.tcx.instantiated_item_has_impossible_predicates((did, substs)) { + tracing::trace!("NoopMethodCall skipped for {:?}: found unsatisfiable predicates", did); + return; + } + let param_env = cx.tcx.param_env(trait_id); // Resolve the trait method instance. let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) else { - return + return; }; // (Re)check that it implements the noop diagnostic. let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return }; @@ -81,14 +99,6 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { return; } let method = &call.ident.name; - let receiver = &elements[0]; - let receiver_ty = cx.typeck_results().expr_ty(receiver); - let expr_ty = cx.typeck_results().expr_ty_adjusted(expr); - if receiver_ty != expr_ty { - // This lint will only trigger if the receiver type and resulting expression \ - // type are the same, implying that the method call is unnecessary. - return; - } let expr_span = expr.span; let note = format!( "the type `{:?}` which `{}` is being called on is the same as \ diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index f76217d921d94..8141dc7e58d05 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -175,7 +175,7 @@ impl<'tcx> MonoItem<'tcx> { MonoItem::GlobalAsm(..) => return true, }; - !tcx.subst_and_check_impossible_predicates((def_id, &substs)) + !tcx.instantiated_item_has_impossible_predicates((def_id, &substs)) } pub fn local_span(&self, tcx: TyCtxt<'tcx>) -> Option { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d8586a8a77df6..772fb081157ce 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1870,10 +1870,38 @@ rustc_queries! { remap_env_constness } - query subst_and_check_impossible_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool { + query instantiated_item_has_impossible_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool { desc { |tcx| - "impossible substituted predicates:`{}`", - tcx.def_path_str(key.0) + "`{}` has impossible predicates after substituting `{:?}`", + tcx.def_path_str(key.0), key.1 + } + } + + // Check if it's even possible to satisfy the 'where' clauses + // for this item, without substitutions. + // + // We don't usually need to worry about this kind of case, + // since we would get a compilation error if the user tried + // to call it. However, since we can do certain mir optimizations + // and lints even without any calls to the function, we need to + // make sure that it even makes sense to try to evaluate predicates + // dependent on the where-clause of this function. + // + // We manually filter the predicates, skipping anything that's not + // "global". We are in a potentially generic context + // (e.g. we are evaluating a function without substituting generic + // parameters), so this filtering serves two purposes: + // + // 1. We skip evaluating any predicates that we would + // never be able prove are unsatisfiable (e.g. `` + // 2. We avoid trying to normalize predicates involving generic + // parameters (e.g. `::MyItem`). This can confuse + // the normalization code (leading to cycle errors), since + // it's usually never invoked in this way. + query item_has_impossible_predicates_for_item(key: DefId) -> bool { + desc { |tcx| + "`{}` has impossible predicates", + tcx.def_path_str(key) } } diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 691f4fb0e5425..05fef53975abd 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -22,7 +22,6 @@ use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeFold use rustc_span::{def_id::DefId, Span}; use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits; use crate::MirPass; use rustc_const_eval::interpret::{ @@ -90,41 +89,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp { return; } - // Check if it's even possible to satisfy the 'where' clauses - // for this item. - // This branch will never be taken for any normal function. - // However, it's possible to `#!feature(trivial_bounds)]` to write - // a function with impossible to satisfy clauses, e.g.: - // `fn foo() where String: Copy {}` - // - // We don't usually need to worry about this kind of case, - // since we would get a compilation error if the user tried - // to call it. However, since we can do const propagation - // even without any calls to the function, we need to make - // sure that it even makes sense to try to evaluate the body. - // If there are unsatisfiable where clauses, then all bets are - // off, and we just give up. - // - // We manually filter the predicates, skipping anything that's not - // "global". We are in a potentially generic context - // (e.g. we are evaluating a function without substituting generic - // parameters, so this filtering serves two purposes: - // - // 1. We skip evaluating any predicates that we would - // never be able prove are unsatisfiable (e.g. `` - // 2. We avoid trying to normalize predicates involving generic - // parameters (e.g. `::MyItem`). This can confuse - // the normalization code (leading to cycle errors), since - // it's usually never invoked in this way. - let predicates = tcx - .predicates_of(def_id.to_def_id()) - .predicates - .iter() - .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); - if traits::impossible_predicates( - tcx, - traits::elaborate_predicates(tcx, predicates).map(|o| o.predicate).collect(), - ) { + if tcx.item_has_impossible_predicates_for_item(def_id) { trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id); return; } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 5e6dabeba6da2..f5aa100932514 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -6,7 +6,6 @@ use rustc_index::vec::Idx; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; -use rustc_middle::traits::ObligationCause; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, ConstKind, Instance, InstanceDef, ParamEnv, Ty, TyCtxt}; use rustc_span::{hygiene::ExpnKind, ExpnData, LocalExpnId, Span}; @@ -74,18 +73,14 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { return false; } - let param_env = tcx.param_env_reveal_all_normalized(def_id); - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let param_env = rustc_trait_selection::traits::normalize_param_env_or_error( - tcx, - def_id.to_def_id(), - param_env, - ObligationCause::misc(body.span, hir_id), - ); + if tcx.item_has_impossible_predicates_for_item(def_id) { + trace!("Inline skipped for {:?}: found unsatisfiable predicates", def_id); + return false; + } let mut this = Inliner { tcx, - param_env, + param_env: tcx.param_env_reveal_all_normalized(def_id), codegen_fn_attrs: tcx.codegen_fn_attrs(def_id), history: Vec::new(), changed: false, diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 8240f5c542a61..f485c86415241 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -454,17 +454,38 @@ pub fn impossible_predicates<'tcx>( result } -fn subst_and_check_impossible_predicates<'tcx>( +fn instantiated_item_has_impossible_predicates<'tcx>( tcx: TyCtxt<'tcx>, key: (DefId, SubstsRef<'tcx>), ) -> bool { - debug!("subst_and_check_impossible_predicates(key={:?})", key); + debug!("instantiated_item_has_impossible_predicates(key={:?})", key); let mut predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates; predicates.retain(|predicate| !predicate.needs_subst()); let result = impossible_predicates(tcx, predicates); - debug!("subst_and_check_impossible_predicates(key={:?}) = {:?}", key, result); + debug!("instantiated_item_has_impossible_predicates(key={:?}) = {:?}", key, result); + result +} + +fn item_has_impossible_predicates_for_item<'tcx>(tcx: TyCtxt<'tcx>, key: DefId) -> bool { + debug!("item_has_impossible_predicates_for_item(key={:?})", key); + + let result = impossible_predicates( + tcx, + elaborate_predicates_with_span( + tcx, + tcx.predicates_of(key) + .predicates + .iter() + .filter(|predicate| predicate.is_global()) + .copied(), + ) + .map(|o| o.predicate) + .collect(), + ); + + debug!("item_has_impossible_predicates_for_item(key={:?}) = {:?}", key, result); result } @@ -847,7 +868,8 @@ pub fn provide(providers: &mut ty::query::Providers) { own_existential_vtable_entries, vtable_entries, vtable_trait_upcasting_coercion_new_vptr_slot, - subst_and_check_impossible_predicates, + instantiated_item_has_impossible_predicates, + item_has_impossible_predicates_for_item, thir_abstract_const: |tcx, def_id| { let def_id = def_id.expect_local(); if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) { diff --git a/src/test/ui/trait-bounds/issue-93008.rs b/src/test/ui/trait-bounds/issue-93008.rs index 1b010566cbc6e..f4d21a160b695 100644 --- a/src/test/ui/trait-bounds/issue-93008.rs +++ b/src/test/ui/trait-bounds/issue-93008.rs @@ -1,10 +1,15 @@ -// compile-flags: -Zmir-opt-level=4 +// build-pass +// compile-flags: -Zmir-opt-level=3 --crate-type=lib -pub fn bar(s: &'static mut ()) +#![feature(trivial_bounds)] +#![allow(trivial_bounds)] + +trait Foo { + fn test(self); +} +fn baz() where - &'static mut (): Clone, //~ ERROR the trait bound + &'static str: Foo, { - <&'static mut () as Clone>::clone(&s); + "Foo".test() } - -fn main() {} diff --git a/src/test/ui/trait-bounds/issue-93008.stderr b/src/test/ui/trait-bounds/issue-93008.stderr deleted file mode 100644 index 10f80f8de0c9b..0000000000000 --- a/src/test/ui/trait-bounds/issue-93008.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0277]: the trait bound `&'static mut (): Clone` is not satisfied - --> $DIR/issue-93008.rs:5:5 - | -LL | &'static mut (): Clone, - | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `&'static mut ()` - | - = help: see issue #48214 - = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/trait-bounds/issue-94680.rs b/src/test/ui/trait-bounds/issue-94680.rs new file mode 100644 index 0000000000000..58e892079e65f --- /dev/null +++ b/src/test/ui/trait-bounds/issue-94680.rs @@ -0,0 +1,14 @@ +// check-pass + +fn main() { + println!("{:?}", { + type T = (); + + pub fn cloneit(it: &'_ mut T) -> (&'_ mut T, &'_ mut T) + where + for<'any> &'any mut T: Clone, + { + (it.clone(), it) + } + }); +} diff --git a/src/test/ui/trait-bounds/issue-94999.rs b/src/test/ui/trait-bounds/issue-94999.rs new file mode 100644 index 0000000000000..777c706dfc1d3 --- /dev/null +++ b/src/test/ui/trait-bounds/issue-94999.rs @@ -0,0 +1,31 @@ +// check-pass + +trait Identity { + type T; +} + +impl Identity for T { + type T = T; +} + +trait Holds { + type Q; +} + + +struct S; +struct X(S); + +struct XHelper; + +impl Holds for X { + type Q = XHelper; +} + +impl Clone for X where >::T: Clone, X: Holds { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +fn main() {}