Skip to content

Commit 8a28c17

Browse files
committed
Instead of checking for exact bounds, try to prove them
1 parent f7fd79a commit 8a28c17

File tree

7 files changed

+213
-77
lines changed

7 files changed

+213
-77
lines changed

compiler/rustc_infer/src/infer/free_regions.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_middle::ty::{self, Lift, Region, TyCtxt};
1111
///
1212
/// This stuff is a bit convoluted and should be refactored, but as we
1313
/// transition to NLL, it'll all go away anyhow.
14-
pub struct RegionRelations<'a, 'tcx> {
14+
pub(crate) struct RegionRelations<'a, 'tcx> {
1515
pub tcx: TyCtxt<'tcx>,
1616

1717
/// The context used for debug messages

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use std::fmt;
2828
/// assuming such values can be found. It returns the final values of
2929
/// all the variables as well as a set of errors that must be reported.
3030
#[instrument(level = "debug", skip(region_rels, var_infos, data))]
31-
pub fn resolve<'tcx>(
31+
pub(crate) fn resolve<'tcx>(
3232
region_rels: &RegionRelations<'_, 'tcx>,
3333
var_infos: VarInfos,
3434
data: RegionConstraintData<'tcx>,

compiler/rustc_typeck/src/check/wfcheck.rs

+102-35
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ use rustc_hir::lang_items::LangItem;
1414
use rustc_hir::ItemKind;
1515
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
1616
use rustc_infer::infer::outlives::obligations::TypeOutlives;
17-
use rustc_infer::infer::TyCtxtInferExt;
1817
use rustc_infer::infer::{self, RegionckMode, SubregionOrigin};
18+
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
19+
use rustc_infer::traits::TraitEngine;
1920
use rustc_middle::hir::map as hir_map;
2021
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst};
2122
use rustc_middle::ty::trait_def::TraitSpecializationKind;
@@ -26,7 +27,9 @@ use rustc_session::parse::feature_err;
2627
use rustc_span::symbol::{sym, Ident, Symbol};
2728
use rustc_span::{Span, DUMMY_SP};
2829
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
29-
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, WellFormedLoc};
30+
use rustc_trait_selection::traits::{
31+
self, ObligationCause, ObligationCauseCode, TraitEngineExt, WellFormedLoc,
32+
};
3033

3134
use std::convert::TryInto;
3235
use std::iter;
@@ -426,42 +429,105 @@ fn check_gat_where_clauses(
426429
}
427430
}
428431

429-
// If there are any missing clauses, emit an error
430-
let mut clauses = clauses.unwrap_or_default();
432+
// If there are any clauses that aren't provable, emit an error
433+
let clauses = clauses.unwrap_or_default();
431434
debug!(?clauses);
432435
if !clauses.is_empty() {
433-
let written_predicates: ty::GenericPredicates<'_> =
434-
tcx.explicit_predicates_of(trait_item.def_id);
435-
let mut clauses: Vec<_> = clauses
436-
.drain_filter(|clause| !written_predicates.predicates.iter().any(|p| &p.0 == clause))
437-
.map(|clause| format!("{}", clause))
438-
.collect();
439-
// We sort so that order is predictable
440-
clauses.sort();
441-
if !clauses.is_empty() {
442-
let mut err = tcx.sess.struct_span_err(
443-
trait_item.span,
444-
&format!("Missing required bounds on {}", trait_item.ident),
445-
);
436+
let param_env = tcx.param_env(trait_item.def_id);
446437

447-
let suggestion = format!(
448-
"{} {}",
449-
if !trait_item.generics.where_clause.predicates.is_empty() {
450-
","
451-
} else {
452-
" where"
453-
},
454-
clauses.join(", "),
455-
);
456-
err.span_suggestion(
457-
trait_item.generics.where_clause.tail_span_for_suggestion(),
458-
"add the required where clauses",
459-
suggestion,
460-
Applicability::MachineApplicable,
461-
);
438+
// This shouldn't really matter, but we need it
439+
let cause = traits::ObligationCause::new(
440+
trait_item.span,
441+
trait_item.hir_id(),
442+
ObligationCauseCode::MiscObligation,
443+
);
444+
// Create an `InferCtxt` to try to prove the clauses we require
445+
tcx.infer_ctxt().enter(|infcx| {
446+
let mut fulfillment_cx = <dyn TraitEngine<'_>>::new(tcx);
447+
448+
// Register all the clauses as obligations
449+
clauses
450+
.clone()
451+
.into_iter()
452+
.map(|predicate| {
453+
traits::Obligation::new(
454+
cause.clone(),
455+
param_env,
456+
predicate,
457+
)
458+
})
459+
.for_each(|obligation| {
460+
fulfillment_cx.register_predicate_obligation(&infcx, obligation)
461+
});
462+
463+
// Convert these obligations into constraints by selecting
464+
let errors = fulfillment_cx.select_all_or_error(&infcx);
465+
if !errors.is_empty() {
466+
bug!("should have only registered region obligations, which get registerd as constraints");
467+
}
462468

463-
err.emit()
464-
}
469+
// FIXME(jackh726): some of this code is shared with `regionctxt`, but in a different
470+
// flow; we could probably better extract the shared logic
471+
472+
// Process the region obligations
473+
let body_id_map = infcx
474+
.inner
475+
.borrow()
476+
.region_obligations()
477+
.iter()
478+
.map(|&(id, _)| (id, vec![]))
479+
.collect();
480+
481+
infcx.process_registered_region_obligations(&body_id_map, None, param_env);
482+
483+
// Resolve the region constraints to find any constraints that we're provable
484+
let outlives_env = OutlivesEnvironment::new(param_env);
485+
let errors = infcx.resolve_regions(trait_item.def_id.to_def_id(), &outlives_env, RegionckMode::default());
486+
487+
// Emit an error if there are non-provable constriants
488+
if !errors.is_empty() {
489+
let mut clauses: Vec<_> = errors.into_iter().map(|error| match error {
490+
RegionResolutionError::ConcreteFailure(_, sup, sub) => format!("{}: {}", sub, sup),
491+
RegionResolutionError::GenericBoundFailure(_, sub, sup) => format!("{}: {}", sub, sup),
492+
_ => bug!("Unexpected region resolution error when resolving outlives lint"),
493+
}).collect();
494+
clauses.sort();
495+
496+
let plural = if clauses.len() > 1 { "s" } else { "" };
497+
let mut err = tcx.sess.struct_span_err(
498+
trait_item.span,
499+
&format!("missing required bound{} on `{}`", plural, trait_item.ident),
500+
);
501+
502+
let suggestion = format!(
503+
"{} {}",
504+
if !trait_item.generics.where_clause.predicates.is_empty() {
505+
","
506+
} else {
507+
" where"
508+
},
509+
clauses.join(", "),
510+
);
511+
err.span_suggestion(
512+
trait_item.generics.where_clause.tail_span_for_suggestion(),
513+
&format!("add the required where clause{}", plural),
514+
suggestion,
515+
Applicability::MachineApplicable,
516+
);
517+
518+
let bound = if clauses.len() > 1 { "these bounds are" } else { "this bound is" };
519+
err.note(
520+
&format!("{} required to ensure that impls have maximum flexibility", bound)
521+
);
522+
err.note(
523+
"see issue #87479 \
524+
<https://github.com/rust-lang/rust/issues/87479> \
525+
for more information",
526+
);
527+
528+
err.emit()
529+
}
530+
});
465531
}
466532
}
467533

@@ -541,7 +607,8 @@ fn region_known_to_outlive<'tcx>(
541607
});
542608

543609
use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate;
544-
(&infcx).push_sub_region_constraint(origin, region_a, region_b);
610+
// `region_a: region_b` -> `region_b <= region_a`
611+
(&infcx).push_sub_region_constraint(origin, region_b, region_a);
545612

546613
let errors = infcx.resolve_regions(
547614
id.expect_owner().to_def_id(),

src/test/ui/generic-associated-types/issue-86787.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ enum Either<L, R> {
99
pub trait HasChildrenOf {
1010
type T;
1111
type TRef<'a>;
12-
//~^ Missing required bounds
12+
//~^ missing required
1313

1414
fn ref_children<'a>(&'a self) -> Vec<Self::TRef<'a>>;
1515
fn take_children(self) -> Vec<Self::T>;
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
error: Missing required bounds on TRef
1+
error: missing required bound on `TRef`
22
--> $DIR/issue-86787.rs:11:5
33
|
44
LL | type TRef<'a>;
55
| ^^^^^^^^^^^^^-
66
| |
7-
| help: add the required where clauses: `where Self: 'a`
7+
| help: add the required where clause: `where Self: 'a`
8+
|
9+
= note: this bound is required to ensure that impls have maximum flexibility
10+
= note: see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
811

912
error: aborting due to previous error
1013

src/test/ui/generic-associated-types/self-outlives-lint.rs

+32-13
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::fmt::Debug;
77
// We have a `&'a self`, so we need a `Self: 'a`
88
trait Iterable {
99
type Item<'x>;
10-
//~^ Missing required bounds
10+
//~^ missing required
1111
fn iter<'a>(&'a self) -> Self::Item<'a>;
1212
}
1313

@@ -23,7 +23,7 @@ impl<T> Iterable for T {
2323
// We have a `&'a T`, so we need a `T: 'x`
2424
trait Deserializer<T> {
2525
type Out<'x>;
26-
//~^ Missing required bounds
26+
//~^ missing required
2727
fn deserialize<'a>(&self, input: &'a T) -> Self::Out<'a>;
2828
}
2929

@@ -37,14 +37,14 @@ impl<T> Deserializer<T> for () {
3737
// We have a `&'b T` and a `'b: 'a`, so it is implied that `T: 'a`. Therefore, we need a `T: 'x`
3838
trait Deserializer2<T> {
3939
type Out<'x>;
40-
//~^ Missing required bounds
40+
//~^ missing required
4141
fn deserialize2<'a, 'b: 'a>(&self, input1: &'b T) -> Self::Out<'a>;
4242
}
4343

4444
// We have a `&'a T` and a `&'b U`, so we need a `T: 'x` and a `U: 'y`
4545
trait Deserializer3<T, U> {
4646
type Out<'x, 'y>;
47-
//~^ Missing required bounds
47+
//~^ missing required
4848
fn deserialize2<'a, 'b>(&self, input: &'a T, input2: &'b U) -> Self::Out<'a, 'b>;
4949
}
5050

@@ -59,7 +59,7 @@ struct Wrap<T>(T);
5959
// We pass `Wrap<T>` and we see `&'z Wrap<T>`, so we require `D: 'x`
6060
trait Des {
6161
type Out<'x, D>;
62-
//~^ Missing required bounds
62+
//~^ missing required
6363
fn des<'z, T>(&self, data: &'z Wrap<T>) -> Self::Out<'z, Wrap<T>>;
6464
}
6565
/*
@@ -75,7 +75,7 @@ impl Des for () {
7575
// implied bound that `T: 'z`, so we require `D: 'x`
7676
trait Des2 {
7777
type Out<'x, D>;
78-
//~^ Missing required bounds
78+
//~^ missing required
7979
fn des<'z, T>(&self, data: &'z Wrap<T>) -> Self::Out<'z, T>;
8080
}
8181
/*
@@ -90,7 +90,7 @@ impl Des2 for () {
9090
// We see `&'z T`, so we require `D: 'x`
9191
trait Des3 {
9292
type Out<'x, D>;
93-
//~^ Missing required bounds
93+
//~^ missing required
9494
fn des<'z, T>(&self, data: &'z T) -> Self::Out<'z, T>;
9595
}
9696
/*
@@ -112,22 +112,22 @@ trait NoGat<'a> {
112112
// FIXME: we require two bounds (`where Self: 'a, Self: 'b`) when we should only require one
113113
trait TraitLifetime<'a> {
114114
type Bar<'b>;
115-
//~^ Missing required bounds
115+
//~^ missing required
116116
fn method(&'a self) -> Self::Bar<'a>;
117117
}
118118

119119
// Like above, but we have a where clause that can prove what we want
120120
// FIXME: we require two bounds (`where Self: 'a, Self: 'b`) when we should only require one
121121
trait TraitLifetimeWhere<'a> where Self: 'a {
122122
type Bar<'b>;
123-
//~^ Missing required bounds
123+
//~^ missing required
124124
fn method(&'a self) -> Self::Bar<'a>;
125125
}
126126

127127
// Explicit bound instead of implicit; we want to still error
128128
trait ExplicitBound {
129129
type Bar<'b>;
130-
//~^ Missing required bounds
130+
//~^ missing required
131131
fn method<'b>(&self, token: &'b ()) -> Self::Bar<'b> where Self: 'b;
132132
}
133133

@@ -141,14 +141,15 @@ trait NotInReturn {
141141
trait IterableTwo {
142142
type Item<'a>;
143143
type Iterator<'a>: Iterator<Item = Self::Item<'a>>;
144-
//~^ Missing required bounds
144+
//~^ missing required
145145
fn iter<'a>(&'a self) -> Self::Iterator<'a>;
146146
}
147147

148-
// We also should report region outlives clauses
148+
// We also should report region outlives clauses. Here, we know that `'y: 'x`,
149+
// because of `&'x &'y`, so we require that `'b: 'a`.
149150
trait RegionOutlives {
150151
type Bar<'a, 'b>;
151-
//~^ Missing required bounds
152+
//~^ missing required
152153
fn foo<'x, 'y>(&self, input: &'x &'y ()) -> Self::Bar<'x, 'y>;
153154
}
154155

@@ -161,6 +162,17 @@ impl Foo for () {
161162
}
162163
*/
163164

165+
// Similar to the above, except with explicit bounds
166+
trait ExplicitRegionOutlives<'ctx> {
167+
type Fut<'out>;
168+
//~^ missing required
169+
170+
fn test<'out>(ctx: &'ctx i32) -> Self::Fut<'out>
171+
where
172+
'ctx: 'out;
173+
}
174+
175+
164176
// If there are multiple methods that return the GAT, require a set of clauses
165177
// that can be satisfied by *all* methods
166178
trait MultipleMethods {
@@ -170,4 +182,11 @@ trait MultipleMethods {
170182
fn gimme_default(&self) -> Self::Bar<'static>;
171183
}
172184

185+
// We would normally require `Self: 'a`, but we can prove that `Self: 'static`
186+
// because of the the bounds on the trait, so the bound is proven
187+
trait Trait: 'static {
188+
type Assoc<'a>;
189+
fn make_assoc(_: &u32) -> Self::Assoc<'_>;
190+
}
191+
173192
fn main() {}

0 commit comments

Comments
 (0)