Skip to content

Commit 6fd51e7

Browse files
Check impossible predicates, use it to skip NoopMethodCall and Inline
1 parent 5cdab3a commit 6fd51e7

File tree

11 files changed

+126
-97
lines changed

11 files changed

+126
-97
lines changed

compiler/rustc_lint/src/noop_method_call.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,16 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
6767
// we need to perform substitution.
6868
return;
6969
}
70+
71+
if cx.tcx.instantiated_item_has_impossible_predicates((did, substs)) {
72+
tracing::trace!("NoopMethodCall skipped for {:?}: found unsatisfiable predicates", did);
73+
return;
74+
}
75+
7076
let param_env = cx.tcx.param_env(trait_id);
7177
// Resolve the trait method instance.
7278
let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) else {
73-
return
79+
return;
7480
};
7581
// (Re)check that it implements the noop diagnostic.
7682
let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return };

compiler/rustc_middle/src/mir/mono.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ impl<'tcx> MonoItem<'tcx> {
175175
MonoItem::GlobalAsm(..) => return true,
176176
};
177177

178-
!tcx.subst_and_check_impossible_predicates((def_id, &substs))
178+
!tcx.instantiated_item_has_impossible_predicates((def_id, &substs))
179179
}
180180

181181
pub fn local_span(&self, tcx: TyCtxt<'tcx>) -> Option<Span> {

compiler/rustc_middle/src/query/mod.rs

+31-3
Original file line numberDiff line numberDiff line change
@@ -1870,10 +1870,38 @@ rustc_queries! {
18701870
remap_env_constness
18711871
}
18721872

1873-
query subst_and_check_impossible_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool {
1873+
query instantiated_item_has_impossible_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool {
18741874
desc { |tcx|
1875-
"impossible substituted predicates:`{}`",
1876-
tcx.def_path_str(key.0)
1875+
"`{}` has impossible predicates after substituting `{:?}`",
1876+
tcx.def_path_str(key.0), key.1
1877+
}
1878+
}
1879+
1880+
// Check if it's even possible to satisfy the 'where' clauses
1881+
// for this item, without substitutions.
1882+
//
1883+
// We don't usually need to worry about this kind of case,
1884+
// since we would get a compilation error if the user tried
1885+
// to call it. However, since we can do certain mir optimizations
1886+
// and lints even without any calls to the function, we need to
1887+
// make sure that it even makes sense to try to evaluate predicates
1888+
// dependent on the where-clause of this function.
1889+
//
1890+
// We manually filter the predicates, skipping anything that's not
1891+
// "global". We are in a potentially generic context
1892+
// (e.g. we are evaluating a function without substituting generic
1893+
// parameters), so this filtering serves two purposes:
1894+
//
1895+
// 1. We skip evaluating any predicates that we would
1896+
// never be able prove are unsatisfiable (e.g. `<T as Foo>`
1897+
// 2. We avoid trying to normalize predicates involving generic
1898+
// parameters (e.g. `<T as Foo>::MyItem`). This can confuse
1899+
// the normalization code (leading to cycle errors), since
1900+
// it's usually never invoked in this way.
1901+
query item_has_impossible_predicates(key: DefId) -> bool {
1902+
desc { |tcx|
1903+
"`{}` has impossible predicates",
1904+
tcx.def_path_str(key)
18771905
}
18781906
}
18791907

compiler/rustc_mir_transform/src/const_prop.rs

+1-36
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeFold
2222
use rustc_span::{def_id::DefId, Span};
2323
use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
2424
use rustc_target::spec::abi::Abi;
25-
use rustc_trait_selection::traits;
2625

2726
use crate::MirPass;
2827
use rustc_const_eval::interpret::{
@@ -90,41 +89,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
9089
return;
9190
}
9291

93-
// Check if it's even possible to satisfy the 'where' clauses
94-
// for this item.
95-
// This branch will never be taken for any normal function.
96-
// However, it's possible to `#!feature(trivial_bounds)]` to write
97-
// a function with impossible to satisfy clauses, e.g.:
98-
// `fn foo() where String: Copy {}`
99-
//
100-
// We don't usually need to worry about this kind of case,
101-
// since we would get a compilation error if the user tried
102-
// to call it. However, since we can do const propagation
103-
// even without any calls to the function, we need to make
104-
// sure that it even makes sense to try to evaluate the body.
105-
// If there are unsatisfiable where clauses, then all bets are
106-
// off, and we just give up.
107-
//
108-
// We manually filter the predicates, skipping anything that's not
109-
// "global". We are in a potentially generic context
110-
// (e.g. we are evaluating a function without substituting generic
111-
// parameters, so this filtering serves two purposes:
112-
//
113-
// 1. We skip evaluating any predicates that we would
114-
// never be able prove are unsatisfiable (e.g. `<T as Foo>`
115-
// 2. We avoid trying to normalize predicates involving generic
116-
// parameters (e.g. `<T as Foo>::MyItem`). This can confuse
117-
// the normalization code (leading to cycle errors), since
118-
// it's usually never invoked in this way.
119-
let predicates = tcx
120-
.predicates_of(def_id.to_def_id())
121-
.predicates
122-
.iter()
123-
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
124-
if traits::impossible_predicates(
125-
tcx,
126-
traits::elaborate_predicates(tcx, predicates).map(|o| o.predicate).collect(),
127-
) {
92+
if tcx.item_has_impossible_predicates(def_id) {
12893
trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id);
12994
return;
13095
}

compiler/rustc_mir_transform/src/const_prop_lint.rs

+1-10
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ use rustc_session::lint;
2424
use rustc_span::{def_id::DefId, Span};
2525
use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
2626
use rustc_target::spec::abi::Abi;
27-
use rustc_trait_selection::traits;
2827

2928
use crate::MirLint;
3029
use rustc_const_eval::const_eval::ConstEvalErr;
@@ -111,15 +110,7 @@ impl<'tcx> MirLint<'tcx> for ConstProp {
111110
// parameters (e.g. `<T as Foo>::MyItem`). This can confuse
112111
// the normalization code (leading to cycle errors), since
113112
// it's usually never invoked in this way.
114-
let predicates = tcx
115-
.predicates_of(def_id.to_def_id())
116-
.predicates
117-
.iter()
118-
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
119-
if traits::impossible_predicates(
120-
tcx,
121-
traits::elaborate_predicates(tcx, predicates).map(|o| o.predicate).collect(),
122-
) {
113+
if tcx.item_has_impossible_predicates(def_id) {
123114
trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id);
124115
return;
125116
}

compiler/rustc_mir_transform/src/inline.rs

+5-10
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use rustc_index::vec::Idx;
66
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
77
use rustc_middle::mir::visit::*;
88
use rustc_middle::mir::*;
9-
use rustc_middle::traits::ObligationCause;
109
use rustc_middle::ty::subst::Subst;
1110
use rustc_middle::ty::{self, ConstKind, Instance, InstanceDef, ParamEnv, Ty, TyCtxt};
1211
use rustc_span::{hygiene::ExpnKind, ExpnData, LocalExpnId, Span};
@@ -74,18 +73,14 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
7473
return false;
7574
}
7675

77-
let param_env = tcx.param_env_reveal_all_normalized(def_id);
78-
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
79-
let param_env = rustc_trait_selection::traits::normalize_param_env_or_error(
80-
tcx,
81-
def_id.to_def_id(),
82-
param_env,
83-
ObligationCause::misc(body.span, hir_id),
84-
);
76+
if tcx.item_has_impossible_predicates(def_id) {
77+
trace!("Inline skipped for {:?}: found unsatisfiable predicates", def_id);
78+
return false;
79+
}
8580

8681
let mut this = Inliner {
8782
tcx,
88-
param_env,
83+
param_env: tcx.param_env_reveal_all_normalized(def_id),
8984
codegen_fn_attrs: tcx.codegen_fn_attrs(def_id),
9085
history: Vec::new(),
9186
changed: false,

compiler/rustc_trait_selection/src/traits/mod.rs

+24-18
Original file line numberDiff line numberDiff line change
@@ -425,17 +425,21 @@ where
425425
/// Normalizes the predicates and checks whether they hold in an empty environment. If this
426426
/// returns true, then either normalize encountered an error or one of the predicates did not
427427
/// hold. Used when creating vtables to check for unsatisfiable methods.
428-
pub fn impossible_predicates<'tcx>(
428+
fn has_impossible_predicates<'tcx>(
429429
tcx: TyCtxt<'tcx>,
430-
predicates: Vec<ty::Predicate<'tcx>>,
430+
predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
431431
) -> bool {
432-
debug!("impossible_predicates(predicates={:?})", predicates);
432+
let predicates = elaborate_predicates(tcx, predicates)
433+
.map(|o| tcx.erase_regions(o.predicate))
434+
.filter(|p| p.is_global())
435+
.collect::<Vec<_>>();
433436

434-
let result = tcx.infer_ctxt().enter(|infcx| {
437+
tcx.infer_ctxt().enter(|infcx| {
435438
let param_env = ty::ParamEnv::reveal_all();
436439
let mut selcx = SelectionContext::new(&infcx);
437440
let mut fulfill_cx = FulfillmentContext::new();
438441
let cause = ObligationCause::dummy();
442+
439443
let Normalized { value: predicates, obligations } =
440444
normalize(&mut selcx, param_env, cause.clone(), predicates);
441445
for obligation in obligations {
@@ -447,24 +451,26 @@ pub fn impossible_predicates<'tcx>(
447451
}
448452

449453
let errors = fulfill_cx.select_all_or_error(&infcx);
450-
451454
!errors.is_empty()
452-
});
453-
debug!("impossible_predicates = {:?}", result);
454-
result
455+
})
455456
}
456457

457-
fn subst_and_check_impossible_predicates<'tcx>(
458+
fn instantiated_item_has_impossible_predicates<'tcx>(
458459
tcx: TyCtxt<'tcx>,
459460
key: (DefId, SubstsRef<'tcx>),
460461
) -> bool {
461-
debug!("subst_and_check_impossible_predicates(key={:?})", key);
462-
463-
let mut predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates;
464-
predicates.retain(|predicate| !predicate.needs_subst());
465-
let result = impossible_predicates(tcx, predicates);
462+
debug!("instantiated_item_has_impossible_predicates(key={:?})", key);
463+
let predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates;
464+
let result = has_impossible_predicates(tcx, predicates.into_iter());
465+
debug!("instantiated_item_has_impossible_predicates(key={:?}) = {:?}", key, result);
466+
result
467+
}
466468

467-
debug!("subst_and_check_impossible_predicates(key={:?}) = {:?}", key, result);
469+
fn item_has_impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, key: DefId) -> bool {
470+
debug!("item_has_impossible_predicates(key={:?})", key);
471+
let predicates = tcx.predicates_of(key).instantiate_identity(tcx).predicates;
472+
let result = has_impossible_predicates(tcx, predicates.into_iter());
473+
debug!("item_has_impossible_predicates(key={:?}) = {:?}", key, result);
468474
result
469475
}
470476

@@ -715,8 +721,7 @@ fn vtable_entries<'tcx>(
715721
// do not hold for this particular set of type parameters.
716722
// Note that this method could then never be called, so we
717723
// do not want to try and codegen it, in that case (see #23435).
718-
let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
719-
if impossible_predicates(tcx, predicates.predicates) {
724+
if tcx.instantiated_item_has_impossible_predicates((def_id, substs)) {
720725
debug!("vtable_entries: predicates do not hold");
721726
return VtblEntry::Vacant;
722727
}
@@ -847,7 +852,8 @@ pub fn provide(providers: &mut ty::query::Providers) {
847852
own_existential_vtable_entries,
848853
vtable_entries,
849854
vtable_trait_upcasting_coercion_new_vptr_slot,
850-
subst_and_check_impossible_predicates,
855+
instantiated_item_has_impossible_predicates,
856+
item_has_impossible_predicates,
851857
thir_abstract_const: |tcx, def_id| {
852858
let def_id = def_id.expect_local();
853859
if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {

src/test/ui/trait-bounds/issue-93008.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
// compile-flags: -Zmir-opt-level=4
1+
// build-pass
2+
// compile-flags: -Zmir-opt-level=3 --crate-type=lib
23

3-
pub fn bar<T>(s: &'static mut ())
4+
#![feature(trivial_bounds)]
5+
#![allow(trivial_bounds)]
6+
7+
trait Foo {
8+
fn test(self);
9+
}
10+
fn baz<T>()
411
where
5-
&'static mut (): Clone, //~ ERROR the trait bound
12+
&'static str: Foo,
613
{
7-
<&'static mut () as Clone>::clone(&s);
14+
"Foo".test()
815
}
9-
10-
fn main() {}

src/test/ui/trait-bounds/issue-93008.stderr

-12
This file was deleted.
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// check-pass
2+
3+
fn main() {
4+
println!("{:?}", {
5+
type T = ();
6+
7+
pub fn cloneit(it: &'_ mut T) -> (&'_ mut T, &'_ mut T)
8+
where
9+
for<'any> &'any mut T: Clone,
10+
{
11+
(it.clone(), it)
12+
}
13+
});
14+
}
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// check-pass
2+
3+
trait Identity<Q> {
4+
type T;
5+
}
6+
7+
impl<Q, T> Identity<Q> for T {
8+
type T = T;
9+
}
10+
11+
trait Holds {
12+
type Q;
13+
}
14+
15+
16+
struct S;
17+
struct X(S);
18+
19+
struct XHelper;
20+
21+
impl Holds for X {
22+
type Q = XHelper;
23+
}
24+
25+
impl<Q> Clone for X where <S as Identity<Q>>::T: Clone, X: Holds<Q = Q> {
26+
fn clone(&self) -> Self {
27+
Self(self.0.clone())
28+
}
29+
}
30+
31+
fn main() {}

0 commit comments

Comments
 (0)