Skip to content

Commit f5193a9

Browse files
committed
Auto merge of #95474 - oli-obk:tait_ub, r=jackh726
Neither require nor imply lifetime bounds on opaque type for well formedness The actual hidden type can live arbitrarily longer than any individual lifetime and arbitrarily shorter than all but one of the lifetimes. fixes #86218 fixes #84305 This is a **breaking change** but it is a necessary soundness fix
2 parents f3fafbb + 59e285f commit f5193a9

34 files changed

+697
-154
lines changed

compiler/rustc_borrowck/src/type_check/free_region_relations.rs

+5
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,11 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
362362
self.region_bound_pairs
363363
.insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a));
364364
}
365+
366+
OutlivesBound::RegionSubOpaque(r_a, def_id, substs) => {
367+
self.region_bound_pairs
368+
.insert(ty::OutlivesPredicate(GenericKind::Opaque(def_id, substs), r_a));
369+
}
365370
}
366371
}
367372
}

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -2481,6 +2481,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
24812481
let labeled_user_string = match bound_kind {
24822482
GenericKind::Param(ref p) => format!("the parameter type `{}`", p),
24832483
GenericKind::Projection(ref p) => format!("the associated type `{}`", p),
2484+
GenericKind::Opaque(def_id, substs) => {
2485+
format!("the opaque type `{}`", self.tcx.def_path_str_with_substs(def_id, substs))
2486+
}
24842487
};
24852488

24862489
if let Some(SubregionOrigin::CompareImplItemObligation {

compiler/rustc_infer/src/infer/outlives/components.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
// RFC for reference.
44

55
use rustc_data_structures::sso::SsoHashSet;
6+
use rustc_hir::def_id::DefId;
67
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
7-
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
8+
use rustc_middle::ty::{self, SubstsRef, Ty, TyCtxt, TypeVisitable};
89
use smallvec::{smallvec, SmallVec};
910

1011
#[derive(Debug)]
@@ -45,6 +46,8 @@ pub enum Component<'tcx> {
4546
// them. This gives us room to improve the regionck reasoning in
4647
// the future without breaking backwards compat.
4748
EscapingProjection(Vec<Component<'tcx>>),
49+
50+
Opaque(DefId, SubstsRef<'tcx>),
4851
}
4952

5053
/// Push onto `out` all the things that must outlive `'a` for the condition
@@ -120,6 +123,17 @@ fn compute_components<'tcx>(
120123
out.push(Component::Param(p));
121124
}
122125

126+
// Ignore lifetimes found in opaque types. Opaque types can
127+
// have lifetimes in their substs which their hidden type doesn't
128+
// actually use. If we inferred that an opaque type is outlived by
129+
// its parameter lifetimes, then we could prove that any lifetime
130+
// outlives any other lifetime, which is unsound.
131+
// See https://github.com/rust-lang/rust/issues/84305 for
132+
// more details.
133+
ty::Opaque(def_id, substs) => {
134+
out.push(Component::Opaque(def_id, substs));
135+
},
136+
123137
// For projections, we prefer to generate an obligation like
124138
// `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
125139
// regionck more ways to prove that it holds. However,
@@ -168,7 +182,6 @@ fn compute_components<'tcx>(
168182
ty::Float(..) | // OutlivesScalar
169183
ty::Never | // ...
170184
ty::Adt(..) | // OutlivesNominalType
171-
ty::Opaque(..) | // OutlivesNominalType (ish)
172185
ty::Foreign(..) | // OutlivesNominalType
173186
ty::Str | // OutlivesScalar (ish)
174187
ty::Slice(..) | // ...

compiler/rustc_infer/src/infer/outlives/env.rs

+4
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ impl<'a, 'tcx> OutlivesEnvironmentBuilder<'tcx> {
142142
self.region_bound_pairs
143143
.insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a));
144144
}
145+
OutlivesBound::RegionSubOpaque(r_a, def_id, substs) => {
146+
self.region_bound_pairs
147+
.insert(ty::OutlivesPredicate(GenericKind::Opaque(def_id, substs), r_a));
148+
}
145149
OutlivesBound::RegionSubRegion(r_a, r_b) => {
146150
if let (ReEarlyBound(_) | ReFree(_), ReVar(vid_b)) = (r_a.kind(), r_b.kind()) {
147151
infcx

compiler/rustc_infer/src/infer/outlives/obligations.rs

+103-41
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,11 @@ use crate::infer::{
6868
};
6969
use crate::traits::{ObligationCause, ObligationCauseCode};
7070
use rustc_data_structures::undo_log::UndoLogs;
71+
use rustc_hir::def_id::DefId;
7172
use rustc_hir::def_id::LocalDefId;
7273
use rustc_middle::mir::ConstraintCategory;
7374
use rustc_middle::ty::subst::GenericArgKind;
74-
use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeVisitable};
75+
use rustc_middle::ty::{self, Region, SubstsRef, Ty, TyCtxt, TypeVisitable};
7576
use smallvec::smallvec;
7677

7778
impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
@@ -283,6 +284,9 @@ where
283284
Component::Param(param_ty) => {
284285
self.param_ty_must_outlive(origin, region, *param_ty);
285286
}
287+
Component::Opaque(def_id, substs) => {
288+
self.opaque_must_outlive(*def_id, substs, origin, region)
289+
}
286290
Component::Projection(projection_ty) => {
287291
self.projection_must_outlive(origin, region, *projection_ty);
288292
}
@@ -314,17 +318,69 @@ where
314318
);
315319

316320
let generic = GenericKind::Param(param_ty);
317-
let verify_bound = self.verify_bound.generic_bound(generic);
321+
let verify_bound = self.verify_bound.param_bound(param_ty);
318322
self.delegate.push_verify(origin, generic, region, verify_bound);
319323
}
320324

325+
#[instrument(level = "debug", skip(self))]
326+
fn opaque_must_outlive(
327+
&mut self,
328+
def_id: DefId,
329+
substs: SubstsRef<'tcx>,
330+
origin: infer::SubregionOrigin<'tcx>,
331+
region: ty::Region<'tcx>,
332+
) {
333+
self.generic_must_outlive(
334+
origin,
335+
region,
336+
GenericKind::Opaque(def_id, substs),
337+
def_id,
338+
substs,
339+
true,
340+
|ty| match *ty.kind() {
341+
ty::Opaque(def_id, substs) => (def_id, substs),
342+
_ => bug!("expected only projection types from env, not {:?}", ty),
343+
},
344+
);
345+
}
346+
321347
#[instrument(level = "debug", skip(self))]
322348
fn projection_must_outlive(
323349
&mut self,
324350
origin: infer::SubregionOrigin<'tcx>,
325351
region: ty::Region<'tcx>,
326352
projection_ty: ty::ProjectionTy<'tcx>,
327353
) {
354+
self.generic_must_outlive(
355+
origin,
356+
region,
357+
GenericKind::Projection(projection_ty),
358+
projection_ty.item_def_id,
359+
projection_ty.substs,
360+
false,
361+
|ty| match ty.kind() {
362+
ty::Projection(projection_ty) => (projection_ty.item_def_id, projection_ty.substs),
363+
_ => bug!("expected only projection types from env, not {:?}", ty),
364+
},
365+
);
366+
}
367+
368+
#[instrument(level = "debug", skip(self, filter))]
369+
fn generic_must_outlive(
370+
&mut self,
371+
origin: infer::SubregionOrigin<'tcx>,
372+
region: ty::Region<'tcx>,
373+
generic: GenericKind<'tcx>,
374+
def_id: DefId,
375+
substs: SubstsRef<'tcx>,
376+
is_opaque: bool,
377+
filter: impl Fn(Ty<'tcx>) -> (DefId, SubstsRef<'tcx>),
378+
) {
379+
// An optimization for a common case with opaque types.
380+
if substs.is_empty() {
381+
return;
382+
}
383+
328384
// This case is thorny for inference. The fundamental problem is
329385
// that there are many cases where we have choice, and inference
330386
// doesn't like choice (the current region inference in
@@ -343,16 +399,15 @@ where
343399
// These are guaranteed to apply, no matter the inference
344400
// results.
345401
let trait_bounds: Vec<_> =
346-
self.verify_bound.projection_declared_bounds_from_trait(projection_ty).collect();
402+
self.verify_bound.declared_region_bounds(def_id, substs).collect();
347403

348404
debug!(?trait_bounds);
349405

350406
// Compute the bounds we can derive from the environment. This
351407
// is an "approximate" match -- in some cases, these bounds
352408
// may not apply.
353-
let mut approx_env_bounds =
354-
self.verify_bound.projection_approx_declared_bounds_from_env(projection_ty);
355-
debug!("projection_must_outlive: approx_env_bounds={:?}", approx_env_bounds);
409+
let mut approx_env_bounds = self.verify_bound.approx_declared_bounds_from_env(generic);
410+
debug!(?approx_env_bounds);
356411

357412
// Remove outlives bounds that we get from the environment but
358413
// which are also deducible from the trait. This arises (cc
@@ -366,14 +421,8 @@ where
366421
// If the declaration is `trait Trait<'b> { type Item: 'b; }`, then `projection_declared_bounds_from_trait`
367422
// will be invoked with `['b => ^1]` and so we will get `^1` returned.
368423
let bound = bound_outlives.skip_binder();
369-
match *bound.0.kind() {
370-
ty::Projection(projection_ty) => self
371-
.verify_bound
372-
.projection_declared_bounds_from_trait(projection_ty)
373-
.all(|r| r != bound.1),
374-
375-
_ => panic!("expected only projection types from env, not {:?}", bound.0),
376-
}
424+
let (def_id, substs) = filter(bound.0);
425+
self.verify_bound.declared_region_bounds(def_id, substs).all(|r| r != bound.1)
377426
});
378427

379428
// If declared bounds list is empty, the only applicable rule is
@@ -390,29 +439,11 @@ where
390439
// the problem is to add `T: 'r`, which isn't true. So, if there are no
391440
// inference variables, we use a verify constraint instead of adding
392441
// edges, which winds up enforcing the same condition.
393-
let needs_infer = projection_ty.needs_infer();
394-
if approx_env_bounds.is_empty() && trait_bounds.is_empty() && needs_infer {
395-
debug!("projection_must_outlive: no declared bounds");
396-
397-
let constraint = origin.to_constraint_category();
398-
for k in projection_ty.substs {
399-
match k.unpack() {
400-
GenericArgKind::Lifetime(lt) => {
401-
self.delegate.push_sub_region_constraint(
402-
origin.clone(),
403-
region,
404-
lt,
405-
constraint,
406-
);
407-
}
408-
GenericArgKind::Type(ty) => {
409-
self.type_must_outlive(origin.clone(), ty, region, constraint);
410-
}
411-
GenericArgKind::Const(_) => {
412-
// Const parameters don't impose constraints.
413-
}
414-
}
415-
}
442+
let needs_infer = substs.needs_infer();
443+
if approx_env_bounds.is_empty() && trait_bounds.is_empty() && (needs_infer || is_opaque) {
444+
debug!("no declared bounds");
445+
446+
self.substs_must_outlive(substs, origin, region);
416447

417448
return;
418449
}
@@ -442,8 +473,8 @@ where
442473
.all(|b| b == Some(trait_bounds[0]))
443474
{
444475
let unique_bound = trait_bounds[0];
445-
debug!("projection_must_outlive: unique trait bound = {:?}", unique_bound);
446-
debug!("projection_must_outlive: unique declared bound appears in trait ref");
476+
debug!(?unique_bound);
477+
debug!("unique declared bound appears in trait ref");
447478
let category = origin.to_constraint_category();
448479
self.delegate.push_sub_region_constraint(origin, region, unique_bound, category);
449480
return;
@@ -454,11 +485,42 @@ where
454485
// projection outlive; in some cases, this may add insufficient
455486
// edges into the inference graph, leading to inference failures
456487
// even though a satisfactory solution exists.
457-
let generic = GenericKind::Projection(projection_ty);
458-
let verify_bound = self.verify_bound.generic_bound(generic);
488+
let verify_bound = self.verify_bound.projection_opaque_bounds(
489+
generic,
490+
def_id,
491+
substs,
492+
&mut Default::default(),
493+
);
459494
debug!("projection_must_outlive: pushing {:?}", verify_bound);
460495
self.delegate.push_verify(origin, generic, region, verify_bound);
461496
}
497+
498+
fn substs_must_outlive(
499+
&mut self,
500+
substs: SubstsRef<'tcx>,
501+
origin: infer::SubregionOrigin<'tcx>,
502+
region: ty::Region<'tcx>,
503+
) {
504+
let constraint = origin.to_constraint_category();
505+
for k in substs {
506+
match k.unpack() {
507+
GenericArgKind::Lifetime(lt) => {
508+
self.delegate.push_sub_region_constraint(
509+
origin.clone(),
510+
region,
511+
lt,
512+
constraint,
513+
);
514+
}
515+
GenericArgKind::Type(ty) => {
516+
self.type_must_outlive(origin.clone(), ty, region, constraint);
517+
}
518+
GenericArgKind::Const(_) => {
519+
// Const parameters don't impose constraints.
520+
}
521+
}
522+
}
523+
}
462524
}
463525

464526
impl<'cx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'tcx> {

0 commit comments

Comments
 (0)