Skip to content

Commit 480799e

Browse files
committed
Add trait obligation tracking to FulfillCtxt and expose FnCtxt in rustc_infer.
Use Lrc and Lock instead of Rc and RefCell.
1 parent 5113ed2 commit 480799e

File tree

7 files changed

+109
-10
lines changed

7 files changed

+109
-10
lines changed

compiler/rustc_hir_typeck/src/lib.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &UnordSet<LocalDef
139139

140140
fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> {
141141
let fallback = move || tcx.type_of(def_id.to_def_id()).instantiate_identity();
142-
typeck_with_fallback(tcx, def_id, fallback)
142+
typeck_with_fallback(tcx, def_id, fallback, None::<&dyn Fn(&FnCtxt<'_, 'tcx>)>)
143143
}
144144

145145
/// Used only to get `TypeckResults` for type inference during error recovery.
@@ -149,14 +149,24 @@ fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::T
149149
let span = tcx.hir().span(tcx.local_def_id_to_hir_id(def_id));
150150
Ty::new_error_with_message(tcx, span, "diagnostic only typeck table used")
151151
};
152-
typeck_with_fallback(tcx, def_id, fallback)
152+
typeck_with_fallback(tcx, def_id, fallback, None::<&dyn Fn(&FnCtxt<'_, 'tcx>)>)
153153
}
154154

155-
#[instrument(level = "debug", skip(tcx, fallback), ret)]
155+
pub fn inspect_typeck<'tcx>(
156+
tcx: TyCtxt<'tcx>,
157+
def_id: LocalDefId,
158+
inspect: impl FnOnce(&FnCtxt<'_, 'tcx>),
159+
) -> &ty::TypeckResults<'tcx> {
160+
let fallback = move || tcx.type_of(def_id.to_def_id()).instantiate_identity();
161+
typeck_with_fallback(tcx, def_id, fallback, Some(inspect))
162+
}
163+
164+
#[instrument(level = "debug", skip(tcx, fallback, inspect_ctxt), ret)]
156165
fn typeck_with_fallback<'tcx>(
157166
tcx: TyCtxt<'tcx>,
158167
def_id: LocalDefId,
159168
fallback: impl Fn() -> Ty<'tcx> + 'tcx,
169+
inspect_ctxt: Option<impl FnOnce(&FnCtxt<'_, 'tcx>)>,
160170
) -> &'tcx ty::TypeckResults<'tcx> {
161171
// Closures' typeck results come from their outermost function,
162172
// as they are part of the same "inference environment".
@@ -277,6 +287,10 @@ fn typeck_with_fallback<'tcx>(
277287
fcx.report_ambiguity_errors();
278288
}
279289

290+
if let Some(inspect) = inspect_ctxt {
291+
inspect(&fcx);
292+
}
293+
280294
if let None = fcx.infcx.tainted_by_errors() {
281295
fcx.check_transmutes();
282296
}

compiler/rustc_infer/src/infer/at.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ impl<'tcx> InferCtxt<'tcx> {
9090
universe: self.universe.clone(),
9191
intercrate,
9292
next_trait_solver: self.next_trait_solver,
93+
fulfilled_obligations: Lrc::clone(&self.fulfilled_obligations),
9394
}
9495
}
9596
}

compiler/rustc_infer/src/infer/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine, Tr
1717

1818
use rustc_data_structures::fx::FxIndexMap;
1919
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
20-
use rustc_data_structures::sync::Lrc;
20+
use rustc_data_structures::sync::{Lock, Lrc};
2121
use rustc_data_structures::undo_log::Rollback;
2222
use rustc_data_structures::unify as ut;
2323
use rustc_errors::{DiagCtxt, DiagnosticBuilder, ErrorGuaranteed};
@@ -334,6 +334,8 @@ pub struct InferCtxt<'tcx> {
334334
pub intercrate: bool,
335335

336336
next_trait_solver: bool,
337+
338+
pub fulfilled_obligations: Lrc<Lock<Vec<traits::FulfilledObligation<'tcx>>>>,
337339
}
338340

339341
impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> {
@@ -708,6 +710,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
708710
universe: Cell::new(ty::UniverseIndex::ROOT),
709711
intercrate,
710712
next_trait_solver,
713+
fulfilled_obligations: Default::default(),
711714
}
712715
}
713716
}

compiler/rustc_infer/src/traits/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ impl<'tcx> PolyTraitObligation<'tcx> {
109109
}
110110
}
111111

112+
pub enum FulfilledObligation<'tcx> {
113+
Success(PredicateObligation<'tcx>),
114+
Failure(FulfillmentError<'tcx>),
115+
}
116+
112117
// `PredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger.
113118
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
114119
static_assert_size!(PredicateObligation<'_>, 48);
@@ -117,6 +122,7 @@ pub type PredicateObligations<'tcx> = Vec<PredicateObligation<'tcx>>;
117122

118123
pub type Selection<'tcx> = ImplSource<'tcx, PredicateObligation<'tcx>>;
119124

125+
#[derive(Clone)]
120126
pub struct FulfillmentError<'tcx> {
121127
pub obligation: PredicateObligation<'tcx>,
122128
pub code: FulfillmentErrorCode<'tcx>,

compiler/rustc_session/src/options.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,6 +1943,9 @@ written to standard error output)"),
19431943
"for every macro invocation, print its name and arguments (default: no)"),
19441944
track_diagnostics: bool = (false, parse_bool, [UNTRACKED],
19451945
"tracks where in rustc a diagnostic was emitted"),
1946+
track_trait_obligations: bool = (false, parse_bool, [TRACKED],
1947+
"tracks fulfilled obligations while trait solving, option is only \
1948+
valid when -Z next-solver=globally (default: no)"),
19461949
// Diagnostics are considered side-effects of a query (see `QuerySideEffects`) and are saved
19471950
// alongside query results and changes to translation options can affect diagnostics - so
19481951
// translation options should be tracked.

compiler/rustc_trait_selection/src/solve/fulfill.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use std::mem;
22

3+
use rustc_data_structures::sync::{Lock, Lrc};
34
use rustc_infer::infer::InferCtxt;
45
use rustc_infer::traits::solve::MaybeCause;
56
use rustc_infer::traits::Obligation;
67
use rustc_infer::traits::{
7-
query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
8-
PredicateObligation, SelectionError, TraitEngine,
8+
query::NoSolution, FulfilledObligation, FulfillmentError, FulfillmentErrorCode,
9+
MismatchedProjectionTypes, PredicateObligation, SelectionError, TraitEngine,
910
};
1011
use rustc_middle::ty;
1112
use rustc_middle::ty::error::{ExpectedFound, TypeError};
@@ -32,11 +33,40 @@ pub struct FulfillmentCtxt<'tcx> {
3233
/// gets rolled back. Because of this we explicitly check that we only
3334
/// use the context in exactly this snapshot.
3435
usable_in_snapshot: usize,
36+
37+
tracked_obligations: Option<Lrc<Lock<Vec<FulfilledObligation<'tcx>>>>>,
3538
}
3639

3740
impl<'tcx> FulfillmentCtxt<'tcx> {
3841
pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> {
39-
FulfillmentCtxt { obligations: Vec::new(), usable_in_snapshot: infcx.num_open_snapshots() }
42+
let tracked_obligations = if infcx.tcx.sess.opts.unstable_opts.track_trait_obligations {
43+
Some(Lrc::clone(&infcx.fulfilled_obligations))
44+
} else {
45+
None
46+
};
47+
48+
FulfillmentCtxt {
49+
obligations: Vec::new(),
50+
usable_in_snapshot: infcx.num_open_snapshots(),
51+
tracked_obligations,
52+
}
53+
}
54+
55+
fn track_fulfillment_errors<'b, 'a: 'b>(
56+
&'a self,
57+
errors: impl IntoIterator<Item = &'b FulfillmentError<'tcx>>,
58+
) {
59+
if let Some(tracked_obligations) = &self.tracked_obligations {
60+
tracked_obligations.borrow_mut().extend(
61+
errors.into_iter().map(|error| FulfilledObligation::Failure(error.clone())),
62+
);
63+
}
64+
}
65+
66+
fn track_fulfillment_success(&self, predicate: &PredicateObligation<'tcx>) {
67+
if let Some(tracked_obligations) = &self.tracked_obligations {
68+
tracked_obligations.borrow_mut().push(FulfilledObligation::Success(predicate.clone()));
69+
}
4070
}
4171
}
4272

@@ -52,7 +82,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
5282
}
5383

5484
fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
55-
self.obligations
85+
let errors = self
86+
.obligations
5687
.drain(..)
5788
.map(|obligation| {
5889
let code = infcx.probe(|_| {
@@ -81,7 +112,11 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
81112
root_obligation: obligation,
82113
}
83114
})
84-
.collect()
115+
.collect();
116+
117+
self.track_fulfillment_errors(&errors);
118+
119+
errors
85120
}
86121

87122
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
@@ -97,7 +132,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
97132
let goal = obligation.clone().into();
98133
let (changed, certainty, nested_goals) =
99134
match infcx.evaluate_root_goal(goal, GenerateProofTree::IfEnabled).0 {
100-
Ok(result) => result,
135+
Ok(result) => {
136+
self.track_fulfillment_success(&obligation);
137+
result
138+
}
101139
Err(NoSolution) => {
102140
errors.push(FulfillmentError {
103141
obligation: obligation.clone(),
@@ -176,6 +214,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
176214
}
177215
}
178216

217+
self.track_fulfillment_errors(&errors);
218+
179219
errors
180220
}
181221

tests/ui/track_trait_obligations.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// compile-flags: -Ztrack-trait-obligations
2+
// run-pass
3+
4+
// Just making sure this flag is accepted and doesn't crash the compiler
5+
use traits::IntoString;
6+
7+
fn does_impl_into_string<T: IntoString>(_: T) {}
8+
9+
fn main() {
10+
let v = vec![(0, 1), (2, 3)];
11+
12+
does_impl_into_string(v);
13+
}
14+
15+
mod traits {
16+
pub trait IntoString {
17+
fn to_string(&self) -> String;
18+
}
19+
20+
impl IntoString for (i32, i32) {
21+
fn to_string(&self) -> String {
22+
format!("({}, {})", self.0, self.1)
23+
}
24+
}
25+
26+
impl<T: IntoString> IntoString for Vec<T> {
27+
fn to_string(&self) -> String {
28+
let s = self.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(", ");
29+
format!("[{s}]")
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)