Skip to content

Commit 97c5793

Browse files
committed
filter trivial predicates when checking methods
1 parent e41380a commit 97c5793

File tree

4 files changed

+139
-6
lines changed

4 files changed

+139
-6
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_hir::{GenericParamKind, ImplItemKind};
1212
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
1313
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
1414
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
15-
use rustc_infer::traits::util;
15+
use rustc_infer::traits::{util, Obligation};
1616
use rustc_middle::ty::error::{ExpectedFound, TypeError};
1717
use rustc_middle::ty::util::ExplicitSelf;
1818
use rustc_middle::ty::{
@@ -190,15 +190,13 @@ fn compare_method_predicate_entailment<'tcx>(
190190
.map(|(predicate, _)| predicate),
191191
);
192192

193+
let caller_bounds = filter_trivial_predicates(tcx, hybrid_preds.predicates);
194+
193195
// Construct trait parameter environment and then shift it into the placeholder viewpoint.
194196
// The key step here is to update the caller_bounds's predicates to be
195197
// the new hybrid bounds we computed.
196198
let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id);
197-
let param_env = ty::ParamEnv::new(
198-
tcx.mk_predicates(&hybrid_preds.predicates),
199-
Reveal::UserFacing,
200-
hir::Constness::NotConst,
201-
);
199+
let param_env = ty::ParamEnv::new(caller_bounds, Reveal::UserFacing, hir::Constness::NotConst);
202200
let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
203201

204202
let infcx = &tcx.infer_ctxt().build();
@@ -2080,3 +2078,83 @@ fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str {
20802078
ty::AssocKind::Type => "type",
20812079
}
20822080
}
2081+
2082+
// FIXME(-Ztrait-solver=next): This hack should be unnecessary with the new trait
2083+
// solver as it is better at dealing with ambiguity.
2084+
//
2085+
// Even if this code isn't completely trivial, it only removes predicates so it
2086+
// should always remain sound.
2087+
#[instrument(level = "debug", skip(tcx, predicates))]
2088+
fn filter_trivial_predicates<'tcx>(
2089+
tcx: TyCtxt<'tcx>,
2090+
mut predicates: Vec<ty::Predicate<'tcx>>,
2091+
) -> &'tcx ty::List<ty::Predicate<'tcx>> {
2092+
// We start with a bad approximation of whether a predicate is trivial and put all
2093+
// non-trivial predicates into the environment used when checking whether the
2094+
// remaining ones are trivial.
2095+
let mut non_trivial_predicates = Vec::new();
2096+
for &predicate in predicates.iter() {
2097+
if !may_be_trivial_predicate(predicate) {
2098+
non_trivial_predicates.push(predicate);
2099+
}
2100+
}
2101+
2102+
let non_trivial_predicates = tcx.mk_predicates(&non_trivial_predicates);
2103+
if non_trivial_predicates.len() == predicates.len() {
2104+
non_trivial_predicates
2105+
} else {
2106+
let param_env =
2107+
ty::ParamEnv::new(non_trivial_predicates, Reveal::UserFacing, hir::Constness::NotConst);
2108+
predicates.retain(|&p| !is_trivial_predicate(tcx, param_env, p));
2109+
tcx.mk_predicates(&predicates)
2110+
}
2111+
}
2112+
2113+
// A bad approximation of whether a predicate is trivial. Used to put all non-trivial
2114+
// predicates into the environment while checking whether the remaining ones are trivial.
2115+
fn may_be_trivial_predicate<'tcx>(predicate: ty::Predicate<'tcx>) -> bool {
2116+
// We only consider trait and projection predicates which don't have a parameter
2117+
// as a self type as potentially non-trivial.
2118+
match predicate.kind().skip_binder() {
2119+
ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
2120+
!matches!(predicate.self_ty().kind(), ty::Param(_))
2121+
}
2122+
ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
2123+
!matches!(predicate.self_ty().kind(), ty::Param(_))
2124+
}
2125+
_ => false,
2126+
}
2127+
}
2128+
2129+
/// Returns whether `predicate` is trivially provable in the empty environment.
2130+
///
2131+
/// While it's definitely trivial if we return `Yes`, this function is incomplete,
2132+
/// so it may incorrectly return `No` even though the `predicate` is actually trivial.
2133+
#[instrument(level = "debug", skip(tcx), ret)]
2134+
fn is_trivial_predicate<'tcx>(
2135+
tcx: TyCtxt<'tcx>,
2136+
param_env: ty::ParamEnv<'tcx>,
2137+
predicate: ty::Predicate<'tcx>,
2138+
) -> bool {
2139+
if !may_be_trivial_predicate(predicate) {
2140+
return false;
2141+
}
2142+
2143+
let infcx = tcx.infer_ctxt().build();
2144+
// HACK: This can overflow and we must not abort here as that would break existing
2145+
// crates, most notably `typenum`.
2146+
//
2147+
// To deal with this we change overflow to only abort trait solving without
2148+
// aborting compilation. This means that this code isn't complete and may
2149+
// incorrectly error which is acceptable as this is just a best effort.
2150+
let ocx = ObligationCtxt::with_query_mode_canonical(&infcx);
2151+
let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate);
2152+
ocx.register_obligation(obligation);
2153+
if ocx.select_all_or_error().is_empty() {
2154+
let outlives_env = OutlivesEnvironment::new(param_env);
2155+
infcx.process_registered_region_obligations(outlives_env.region_bound_pairs(), param_env);
2156+
infcx.resolve_regions(&outlives_env).is_empty()
2157+
} else {
2158+
false
2159+
}
2160+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
// check-pass
3+
4+
use std::borrow::Cow;
5+
6+
pub trait Trait {
7+
fn method(self) -> Option<Cow<'static, str>>
8+
where
9+
Self: Sized;
10+
}
11+
12+
impl<'a> Trait for Cow<'a, str> {
13+
// We have to check `WF(return-type)` which requires `Cow<'static, str>: Sized`.
14+
// If we use the `Self: Sized` bound from the trait method we end up equating
15+
// `Cow<'a, str>` with `Cow<'static, str>`, causing an error.
16+
fn method(self) -> Option<Cow<'static, str>> {
17+
None
18+
}
19+
}
20+
21+
fn main() {}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// check-pass
2+
3+
// Similar to issue-108544.rs except that we have a generic `T` which
4+
// previously caused an overeager fast-path to trigger.
5+
use std::borrow::Cow;
6+
7+
pub trait Trait<T: Clone> {
8+
fn method(self) -> Option<Cow<'static, T>>
9+
where
10+
Self: Sized;
11+
}
12+
13+
impl<'a, T: Clone> Trait<T> for Cow<'a, T> {
14+
fn method(self) -> Option<Cow<'static, T>> {
15+
None
16+
}
17+
}
18+
19+
fn main() {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// check-pass
2+
3+
pub trait Trait<'a, 'b> {
4+
fn method(self, _: &'static &'static ())
5+
where
6+
'a: 'b;
7+
}
8+
9+
impl<'a> Trait<'a, 'static> for () {
10+
// On first glance, this seems like we have the extra implied bound that
11+
// `'a: 'static`, but we know this from the trait method where clause.
12+
fn method(self, _: &'static &'a ()) {}
13+
}
14+
15+
fn main() {}

0 commit comments

Comments
 (0)