Skip to content

Commit 77a6e85

Browse files
committed
Make codegen_fulfill_obligation a canonical query and rename it to
`resolve_vtable`.
1 parent 5a5a8cf commit 77a6e85

File tree

12 files changed

+127
-127
lines changed

12 files changed

+127
-127
lines changed

src/librustc/arena.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ macro_rules! arena_types {
6565
Vec<rustc::traits::query::outlives_bounds::OutlivesBound<'tcx>>
6666
>
6767
>,
68+
[] resolve_vtables:
69+
rustc::infer::canonical::Canonical<'tcx,
70+
rustc::infer::canonical::QueryResponse<'tcx,
71+
rustc::traits::Vtable<'tcx, ()>
72+
>
73+
>,
6874
[] type_op_subtype:
6975
rustc::infer::canonical::Canonical<'tcx,
7076
rustc::infer::canonical::QueryResponse<'tcx, ()>

src/librustc/dep_graph/dep_node.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ use crate::mir;
5555
use crate::mir::interpret::ConstEvalInput;
5656
use crate::traits;
5757
use crate::traits::query::{
58-
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
58+
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTraitGoal, CanonicalTyGoal,
5959
CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal,
6060
CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal,
6161
};

src/librustc/query/mod.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::mir;
33
use crate::mir::interpret::ConstEvalInput;
44
use crate::traits;
55
use crate::traits::query::{
6-
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
6+
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTraitGoal, CanonicalTyGoal,
77
CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal,
88
CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal,
99
};
@@ -592,17 +592,15 @@ rustc_queries! {
592592
no_force
593593
desc { |tcx| "finding all methods for trait {}", tcx.def_path_str(key.def_id()) }
594594
}
595-
}
596595

597-
Codegen {
598-
query codegen_fulfill_obligation(
599-
key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)
600-
) -> Vtable<'tcx, ()> {
596+
/// Do not call this query directly: invoke `infcx.resolve_vtable()` instead.
597+
query resolve_vtable(
598+
goal: CanonicalTraitGoal<'tcx>
599+
) -> Result<&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vtable<'tcx, ()>>>, NoSolution> {
601600
no_force
602-
cache_on_disk_if { true }
603601
desc { |tcx|
604-
"checking if `{}` fulfills its obligations",
605-
tcx.def_path_str(key.1.def_id())
602+
"resolving vtable for `{}`",
603+
tcx.def_path_str(goal.value.value.def_id())
606604
}
607605
}
608606
}

src/librustc/traits/codegen/mod.rs

Lines changed: 0 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -3,79 +3,10 @@
33
// seems likely that they should eventually be merged into more
44
// general routines.
55

6-
use crate::infer::InferCtxt;
7-
use crate::traits::{
8-
FulfillmentContext, Obligation, ObligationCause, SelectionContext, TraitEngine, Vtable,
9-
};
106
use crate::ty::fold::TypeFoldable;
117
use crate::ty::subst::{Subst, SubstsRef};
128
use crate::ty::{self, TyCtxt};
139

14-
/// Attempts to resolve an obligation to a vtable. The result is
15-
/// a shallow vtable resolution, meaning that we do not
16-
/// (necessarily) resolve all nested obligations on the impl. Note
17-
/// that type check should guarantee to us that all nested
18-
/// obligations *could be* resolved if we wanted to.
19-
/// Assumes that this is run after the entire crate has been successfully type-checked.
20-
pub fn codegen_fulfill_obligation<'tcx>(
21-
ty: TyCtxt<'tcx>,
22-
(param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>),
23-
) -> Vtable<'tcx, ()> {
24-
// Remove any references to regions; this helps improve caching.
25-
let trait_ref = ty.erase_regions(&trait_ref);
26-
27-
debug!(
28-
"codegen_fulfill_obligation(trait_ref={:?}, def_id={:?})",
29-
(param_env, trait_ref),
30-
trait_ref.def_id()
31-
);
32-
33-
// Do the initial selection for the obligation. This yields the
34-
// shallow result we are looking for -- that is, what specific impl.
35-
ty.infer_ctxt().enter(|infcx| {
36-
let mut selcx = SelectionContext::new(&infcx);
37-
38-
let obligation_cause = ObligationCause::dummy();
39-
let obligation =
40-
Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate());
41-
42-
let selection = match selcx.select(&obligation) {
43-
Ok(Some(selection)) => selection,
44-
Ok(None) => {
45-
// Ambiguity can happen when monomorphizing during trans
46-
// expands to some humongo type that never occurred
47-
// statically -- this humongo type can then overflow,
48-
// leading to an ambiguous result. So report this as an
49-
// overflow bug, since I believe this is the only case
50-
// where ambiguity can result.
51-
bug!(
52-
"Encountered ambiguity selecting `{:?}` during codegen, \
53-
presuming due to overflow",
54-
trait_ref
55-
)
56-
}
57-
Err(e) => {
58-
bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref)
59-
}
60-
};
61-
62-
debug!("fulfill_obligation: selection={:?}", selection);
63-
64-
// Currently, we use a fulfillment context to completely resolve
65-
// all nested obligations. This is because they can inform the
66-
// inference of the impl's type parameters.
67-
let mut fulfill_cx = FulfillmentContext::new();
68-
let vtable = selection.map(|predicate| {
69-
debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate);
70-
fulfill_cx.register_predicate_obligation(&infcx, predicate);
71-
});
72-
let vtable = infcx.drain_fulfillment_cx_or_panic(&mut fulfill_cx, &vtable);
73-
74-
info!("Cache miss: {:?} => {:?}", trait_ref, vtable);
75-
vtable
76-
})
77-
}
78-
7910
impl<'tcx> TyCtxt<'tcx> {
8011
/// Monomorphizes a type from the AST by first applying the
8112
/// in-scope substitutions and then normalizing any associated
@@ -100,37 +31,3 @@ impl<'tcx> TyCtxt<'tcx> {
10031
self.normalize_erasing_regions(param_env, substituted)
10132
}
10233
}
103-
104-
// # Global Cache
105-
106-
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
107-
/// Finishes processes any obligations that remain in the
108-
/// fulfillment context, and then returns the result with all type
109-
/// variables removed and regions erased. Because this is intended
110-
/// for use after type-check has completed, if any errors occur,
111-
/// it will panic. It is used during normalization and other cases
112-
/// where processing the obligations in `fulfill_cx` may cause
113-
/// type inference variables that appear in `result` to be
114-
/// unified, and hence we need to process those obligations to get
115-
/// the complete picture of the type.
116-
fn drain_fulfillment_cx_or_panic<T>(
117-
&self,
118-
fulfill_cx: &mut FulfillmentContext<'tcx>,
119-
result: &T,
120-
) -> T
121-
where
122-
T: TypeFoldable<'tcx>,
123-
{
124-
debug!("drain_fulfillment_cx_or_panic()");
125-
126-
// In principle, we only need to do this so long as `result`
127-
// contains unbound type parameters. It could be a slight
128-
// optimization to stop iterating early.
129-
if let Err(errors) = fulfill_cx.select_all_or_error(self) {
130-
bug!("Encountered errors `{:?}` resolving bounds after type-checking", errors);
131-
}
132-
133-
let result = self.resolve_vars_if_possible(result);
134-
self.tcx.erase_regions(&result)
135-
}
136-
}

src/librustc/traits/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1235,7 +1235,6 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
12351235
is_object_safe: object_safety::is_object_safe_provider,
12361236
specialization_graph_of: specialize::specialization_graph_provider,
12371237
specializes: specialize::specializes,
1238-
codegen_fulfill_obligation: codegen::codegen_fulfill_obligation,
12391238
vtable_methods,
12401239
substitute_normalize_and_test_predicates,
12411240
..*providers

src/librustc/traits/query/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub mod method_autoderef;
1515
pub mod normalize;
1616
pub mod normalize_erasing_regions;
1717
pub mod outlives_bounds;
18+
pub mod resolve_vtable;
1819
pub mod type_op;
1920

2021
pub type CanonicalProjectionGoal<'tcx> =
@@ -24,6 +25,8 @@ pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>
2425

2526
pub type CanonicalPredicateGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>;
2627

28+
pub type CanonicalTraitGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::PolyTraitRef<'tcx>>>;
29+
2730
pub type CanonicalTypeOpAscribeUserTypeGoal<'tcx> =
2831
Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::ascribe_user_type::AscribeUserType<'tcx>>>;
2932

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use crate::infer::canonical::OriginalQueryValues;
2+
use crate::infer::InferCtxt;
3+
use crate::traits::{ObligationCause, Vtable};
4+
use crate::ty;
5+
6+
impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
7+
pub fn resolve_vtable(
8+
&self,
9+
param_env: ty::ParamEnv<'tcx>,
10+
trait_ref: ty::TraitRef<'tcx>,
11+
) -> Option<Vtable<'tcx, ()>> {
12+
// Remove any references to regions; this helps improve caching.
13+
let trait_ref = self.tcx.erase_regions(&trait_ref);
14+
15+
let mut orig_values = OriginalQueryValues::default();
16+
let canonical =
17+
self.canonicalize_query(&param_env.and(ty::Binder::bind(trait_ref)), &mut orig_values);
18+
if let Ok(query_response) = self.tcx.resolve_vtable(canonical) {
19+
if query_response.is_proven() {
20+
let result = self.instantiate_query_response_and_region_obligations(
21+
&ObligationCause::dummy(),
22+
param_env,
23+
&orig_values,
24+
query_response,
25+
);
26+
return Some(result.unwrap().value);
27+
}
28+
};
29+
None
30+
}
31+
}

src/librustc/ty/instance.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,8 @@ fn resolve_associated_item<'infcx, 'tcx>(
397397
);
398398

399399
let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs);
400-
let vtbl = tcx.codegen_fulfill_obligation((param_env, ty::Binder::bind(trait_ref)));
400+
401+
let vtbl = infcx.resolve_vtable(param_env, trait_ref)?;
401402

402403
// Now that we know which impl is being used, we can dispatch to
403404
// the actual function:

src/librustc/ty/query/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::traits::query::method_autoderef::MethodAutoderefStepsResult;
2222
use crate::traits::query::normalize::NormalizationResult;
2323
use crate::traits::query::outlives_bounds::OutlivesBound;
2424
use crate::traits::query::{
25-
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
25+
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTraitGoal, CanonicalTyGoal,
2626
CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal,
2727
CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, NoSolution,
2828
};

src/librustc_mir/monomorphize/mod.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@ pub fn custom_coerce_unsize_info<'tcx>(
1212
) -> CustomCoerceUnsized {
1313
let def_id = tcx.lang_items().coerce_unsized_trait().unwrap();
1414

15-
let trait_ref = ty::Binder::bind(ty::TraitRef {
16-
def_id: def_id,
17-
substs: tcx.mk_substs_trait(source_ty, &[target_ty.into()]),
18-
});
15+
let trait_ref =
16+
ty::TraitRef { def_id, substs: tcx.mk_substs_trait(source_ty, &[target_ty.into()]) };
1917

20-
match tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)) {
21-
traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => {
22-
tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap()
23-
}
24-
vtable => {
25-
bug!("invalid `CoerceUnsized` vtable: {:?}", vtable);
18+
let impl_def_id = tcx.infer_ctxt().enter(|ref infcx| {
19+
match infcx.resolve_vtable(ty::ParamEnv::reveal_all(), trait_ref).unwrap() {
20+
traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => impl_def_id,
21+
vtable => {
22+
bug!("invalid `CoerceUnsized` vtable: {:?}", vtable);
23+
}
2624
}
27-
}
25+
});
26+
27+
tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap()
2828
}

src/librustc_traits/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ mod implied_outlives_bounds;
1919
pub mod lowering;
2020
mod normalize_erasing_regions;
2121
mod normalize_projection_ty;
22+
mod resolve_vtable;
2223
mod type_op;
2324

2425
use rustc::ty::query::Providers;
@@ -31,5 +32,6 @@ pub fn provide(p: &mut Providers<'_>) {
3132
chalk_context::provide(p);
3233
normalize_projection_ty::provide(p);
3334
normalize_erasing_regions::provide(p);
35+
resolve_vtable::provide(p);
3436
type_op::provide(p);
3537
}

src/librustc_traits/resolve_vtable.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use rustc::infer::canonical::{Canonical, QueryResponse};
2+
use rustc::traits::query::{CanonicalTraitGoal, NoSolution};
3+
use rustc::traits::{Obligation, ObligationCause, SelectionContext, TraitQueryMode, Vtable};
4+
use rustc::ty::query::Providers;
5+
use rustc::ty::{ParamEnvAnd, TyCtxt};
6+
7+
crate fn provide(p: &mut Providers<'_>) {
8+
*p = Providers { resolve_vtable, ..*p };
9+
}
10+
11+
/// Attempts to resolve a vtable.
12+
pub fn resolve_vtable<'tcx>(
13+
tcx: TyCtxt<'tcx>,
14+
canonical_goal: CanonicalTraitGoal<'tcx>,
15+
) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, Vtable<'tcx, ()>>>, NoSolution> {
16+
// Do the initial selection for the obligation. This yields the
17+
// shallow result we are looking for -- that is, what specific impl.
18+
tcx.infer_ctxt().enter_canonical_trait_query(
19+
&canonical_goal,
20+
|infcx, fulfill_cx, ParamEnvAnd { param_env, value: trait_ref }| {
21+
debug!(
22+
"resolve_vtable(param_env={:?}, trait_ref={:?}, def_id={:?})",
23+
param_env,
24+
trait_ref,
25+
trait_ref.def_id()
26+
);
27+
28+
let mut selcx = SelectionContext::with_query_mode(&infcx, TraitQueryMode::Canonical);
29+
30+
let obligation_cause = ObligationCause::dummy();
31+
let obligation =
32+
Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate());
33+
34+
let selection = match selcx.select(&obligation) {
35+
Ok(Some(selection)) => selection,
36+
Ok(None) => return Err(NoSolution),
37+
Err(e) => {
38+
debug!(
39+
"Encountered error `{:?}` when resolving vtable for `{:?}`",
40+
e, trait_ref
41+
);
42+
return Err(NoSolution);
43+
}
44+
};
45+
46+
debug!("resolve_vtable: selection={:?}", selection);
47+
48+
// Currently, we use a fulfillment context to completely resolve
49+
// all nested obligations. This is because they can inform the
50+
// inference of the impl's type parameters.
51+
let mut vtable = selection.map(|predicate| {
52+
debug!("resolve_vtable: register_predicate_obligation {:?}", predicate);
53+
fulfill_cx.register_predicate_obligation(&infcx, predicate);
54+
});
55+
56+
vtable = infcx.resolve_vars_if_possible(&vtable);
57+
vtable = tcx.erase_regions(&vtable);
58+
59+
info!("Cache miss: {:?} => {:?}", trait_ref, vtable);
60+
Ok(vtable)
61+
},
62+
)
63+
}

0 commit comments

Comments
 (0)