1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
use rustc_infer::infer::canonical::Canonical;
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
use rustc_infer::traits::query::OutlivesBound;
use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
use rustc_middle::infer::canonical::CanonicalQueryResponse;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt};
use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::DUMMY_SP;
use rustc_type_ir::outlives::{push_outlives_components, Component};
use smallvec::{smallvec, SmallVec};
use tracing::debug;
use crate::traits::query::NoSolution;
use crate::traits::{wf, ObligationCtxt};
#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
pub struct ImpliedOutlivesBounds<'tcx> {
pub ty: Ty<'tcx>,
}
impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> {
type QueryResponse = Vec<OutlivesBound<'tcx>>;
fn try_fast_path(
_tcx: TyCtxt<'tcx>,
key: &ParamEnvAnd<'tcx, Self>,
) -> Option<Self::QueryResponse> {
// Don't go into the query for things that can't possibly have lifetimes.
match key.value.ty.kind() {
ty::Tuple(elems) if elems.is_empty() => Some(vec![]),
ty::Never | ty::Str | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => {
Some(vec![])
}
_ => None,
}
}
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> {
// FIXME this `unchecked_map` is only necessary because the
// query is defined as taking a `ParamEnvAnd<Ty>`; it should
// take an `ImpliedOutlivesBounds` instead
let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| {
let ImpliedOutlivesBounds { ty } = value;
param_env.and(ty)
});
if tcx.sess.opts.unstable_opts.no_implied_bounds_compat {
tcx.implied_outlives_bounds(canonicalized)
} else {
tcx.implied_outlives_bounds_compat(canonicalized)
}
}
fn perform_locally_with_next_solver(
ocx: &ObligationCtxt<'_, 'tcx>,
key: ParamEnvAnd<'tcx, Self>,
) -> Result<Self::QueryResponse, NoSolution> {
if ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat {
compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty)
} else {
compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty)
}
}
}
pub fn compute_implied_outlives_bounds_inner<'tcx>(
ocx: &ObligationCtxt<'_, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
let normalize_op = |ty| {
let ty = ocx.normalize(&ObligationCause::dummy(), param_env, ty);
if !ocx.select_all_or_error().is_empty() {
return Err(NoSolution);
}
let ty = ocx.infcx.resolve_vars_if_possible(ty);
let ty = OpportunisticRegionResolver::new(&ocx.infcx).fold_ty(ty);
Ok(ty)
};
// Sometimes when we ask what it takes for T: WF, we get back that
// U: WF is required; in that case, we push U onto this stack and
// process it next. Because the resulting predicates aren't always
// guaranteed to be a subset of the original type, so we need to store the
// WF args we've computed in a set.
let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
let mut wf_args = vec![ty.into(), normalize_op(ty)?.into()];
let mut outlives_bounds: Vec<OutlivesBound<'tcx>> = vec![];
while let Some(arg) = wf_args.pop() {
if !checked_wf_args.insert(arg) {
continue;
}
// From the full set of obligations, just filter down to the region relationships.
for obligation in
wf::unnormalized_obligations(ocx.infcx, param_env, arg).into_iter().flatten()
{
assert!(!obligation.has_escaping_bound_vars());
let Some(pred) = obligation.predicate.kind().no_bound_vars() else {
continue;
};
match pred {
// FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound
// if we ever support that
ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::NormalizesTo(..)
| ty::PredicateKind::AliasRelate(..) => {}
// We need to search through *all* WellFormed predicates
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
wf_args.push(arg);
}
// We need to register region relationships
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(
ty::OutlivesPredicate(r_a, r_b),
)) => outlives_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)),
ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
ty_a,
r_b,
))) => {
let ty_a = normalize_op(ty_a)?;
let mut components = smallvec![];
push_outlives_components(ocx.infcx.tcx, ty_a, &mut components);
outlives_bounds.extend(implied_bounds_from_components(r_b, components))
}
}
}
}
Ok(outlives_bounds)
}
pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
ocx: &ObligationCtxt<'_, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
let tcx = ocx.infcx.tcx;
// Sometimes when we ask what it takes for T: WF, we get back that
// U: WF is required; in that case, we push U onto this stack and
// process it next. Because the resulting predicates aren't always
// guaranteed to be a subset of the original type, so we need to store the
// WF args we've computed in a set.
let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
let mut wf_args = vec![ty.into()];
let mut outlives_bounds: Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>> = vec![];
while let Some(arg) = wf_args.pop() {
if !checked_wf_args.insert(arg) {
continue;
}
// Compute the obligations for `arg` to be well-formed. If `arg` is
// an unresolved inference variable, just instantiated an empty set
// -- because the return type here is going to be things we *add*
// to the environment, it's always ok for this set to be smaller
// than the ultimate set. (Note: normally there won't be
// unresolved inference variables here anyway, but there might be
// during typeck under some circumstances.)
//
// FIXME(@lcnr): It's not really "always fine", having fewer implied
// bounds can be backward incompatible, e.g. #101951 was caused by
// us not dealing with inference vars in `TypeOutlives` predicates.
let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP)
.unwrap_or_default();
for obligation in obligations {
debug!(?obligation);
assert!(!obligation.has_escaping_bound_vars());
// While these predicates should all be implied by other parts of
// the program, they are still relevant as they may constrain
// inference variables, which is necessary to add the correct
// implied bounds in some cases, mostly when dealing with projections.
//
// Another important point here: we only register `Projection`
// predicates, since otherwise we might register outlives
// predicates containing inference variables, and we don't
// learn anything new from those.
if obligation.predicate.has_non_region_infer() {
match obligation.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
| ty::PredicateKind::AliasRelate(..) => {
ocx.register_obligation(obligation.clone());
}
_ => {}
}
}
let pred = match obligation.predicate.kind().no_bound_vars() {
None => continue,
Some(pred) => pred,
};
match pred {
// FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound
// if we ever support that
ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::NormalizesTo(..)
| ty::PredicateKind::AliasRelate(..) => {}
// We need to search through *all* WellFormed predicates
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
wf_args.push(arg);
}
// We need to register region relationships
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(
ty::OutlivesPredicate(r_a, r_b),
)) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)),
ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
ty_a,
r_b,
))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)),
}
}
}
// This call to `select_all_or_error` is necessary to constrain inference variables, which we
// use further down when computing the implied bounds.
match ocx.select_all_or_error().as_slice() {
[] => (),
_ => return Err(NoSolution),
}
// We lazily compute the outlives components as
// `select_all_or_error` constrains inference variables.
let mut implied_bounds = Vec::new();
for ty::OutlivesPredicate(a, r_b) in outlives_bounds {
match a.unpack() {
ty::GenericArgKind::Lifetime(r_a) => {
implied_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a))
}
ty::GenericArgKind::Type(ty_a) => {
let mut ty_a = ocx.infcx.resolve_vars_if_possible(ty_a);
// Need to manually normalize in the new solver as `wf::obligations` does not.
if ocx.infcx.next_trait_solver() {
ty_a = ocx
.deeply_normalize(&ObligationCause::dummy(), param_env, ty_a)
.map_err(|_| NoSolution)?;
}
let mut components = smallvec![];
push_outlives_components(tcx, ty_a, &mut components);
implied_bounds.extend(implied_bounds_from_components(r_b, components))
}
ty::GenericArgKind::Const(_) => {
unreachable!("consts do not participate in outlives bounds")
}
}
}
Ok(implied_bounds)
}
/// When we have an implied bound that `T: 'a`, we can further break
/// this down to determine what relationships would have to hold for
/// `T: 'a` to hold. We get to assume that the caller has validated
/// those relationships.
fn implied_bounds_from_components<'tcx>(
sub_region: ty::Region<'tcx>,
sup_components: SmallVec<[Component<TyCtxt<'tcx>>; 4]>,
) -> Vec<OutlivesBound<'tcx>> {
sup_components
.into_iter()
.filter_map(|component| {
match component {
Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)),
Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)),
Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)),
Component::Placeholder(_p) => {
// FIXME(non_lifetime_binders): Placeholders don't currently
// imply anything for outlives, though they could easily.
None
}
Component::EscapingAlias(_) =>
// If the projection has escaping regions, don't
// try to infer any implied bounds even for its
// free components. This is conservative, because
// the caller will still have to prove that those
// free components outlive `sub_region`. But the
// idea is that the WAY that the caller proves
// that may change in the future and we want to
// give ourselves room to get smarter here.
{
None
}
Component::UnresolvedInferenceVariable(..) => None,
}
})
.collect()
}