Skip to content

Commit 9bcfdb7

Browse files
committed
Move projection_mode to InferContext rather than SelectionContext to reduce chance of bugs
1 parent 386f8ee commit 9bcfdb7

File tree

27 files changed

+225
-184
lines changed

27 files changed

+225
-184
lines changed

src/librustc/middle/check_match.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use middle::expr_use_visitor as euv;
2525
use middle::infer;
2626
use middle::mem_categorization::{cmt};
2727
use middle::pat_util::*;
28+
use middle::traits::ProjectionMode;
2829
use middle::ty::*;
2930
use middle::ty;
3031
use std::cmp::Ordering;
@@ -1101,7 +1102,8 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
11011102
//FIXME: (@jroesch) this code should be floated up as well
11021103
let infcx = infer::new_infer_ctxt(cx.tcx,
11031104
&cx.tcx.tables,
1104-
Some(cx.param_env.clone()));
1105+
Some(cx.param_env.clone()),
1106+
ProjectionMode::AnyFinal);
11051107
if infcx.type_moves_by_default(pat_ty, pat.span) {
11061108
check_move(p, sub.as_ref().map(|p| &**p));
11071109
}
@@ -1133,7 +1135,8 @@ fn check_for_mutation_in_guard<'a, 'tcx>(cx: &'a MatchCheckCtxt<'a, 'tcx>,
11331135

11341136
let infcx = infer::new_infer_ctxt(cx.tcx,
11351137
&cx.tcx.tables,
1136-
Some(checker.cx.param_env.clone()));
1138+
Some(checker.cx.param_env.clone()),
1139+
ProjectionMode::AnyFinal);
11371140

11381141
let mut visitor = ExprUseVisitor::new(&mut checker, &infcx);
11391142
visitor.walk_expr(guard);

src/librustc/middle/const_eval.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use middle::def_id::DefId;
2424
use middle::pat_util::def_to_path;
2525
use middle::ty::{self, Ty, TyCtxt};
2626
use middle::ty::util::IntTypeExt;
27+
use middle::traits::ProjectionMode;
2728
use middle::astconv_util::ast_ty_to_prim_ty;
2829
use util::nodemap::NodeMap;
2930

@@ -1049,7 +1050,7 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>,
10491050
trait_ref);
10501051

10511052
tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id());
1052-
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
1053+
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal);
10531054

10541055
let mut selcx = traits::SelectionContext::new(&infcx);
10551056
let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
@@ -1067,6 +1068,11 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>,
10671068
}
10681069
};
10691070

1071+
// NOTE: this code does not currently account for specialization, but when
1072+
// it does so, it should hook into the ProjectionMode to determine when the
1073+
// constant should resolve; this will also require plumbing through to this
1074+
// function whether we are in "trans mode" to pick the right ProjectionMode
1075+
// when constructing the inference context above.
10701076
match selection {
10711077
traits::VtableImpl(ref impl_data) => {
10721078
match tcx.associated_consts(impl_data.impl_def_id)

src/librustc/middle/infer/mod.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use middle::region::CodeExtent;
2727
use middle::subst;
2828
use middle::subst::Substs;
2929
use middle::subst::Subst;
30-
use middle::traits;
30+
use middle::traits::{self, ProjectionMode};
3131
use middle::ty::adjustment;
3232
use middle::ty::{TyVid, IntVid, FloatVid};
3333
use middle::ty::{self, Ty, TyCtxt};
@@ -99,6 +99,11 @@ pub struct InferCtxt<'a, 'tcx: 'a> {
9999
// directly.
100100
normalize: bool,
101101

102+
// Sadly, the behavior of projection varies a bit depending on the
103+
// stage of compilation. The specifics are given in the
104+
// documentation for `ProjectionMode`.
105+
projection_mode: ProjectionMode,
106+
102107
err_count_on_creation: usize,
103108
}
104109

@@ -354,7 +359,8 @@ pub fn fixup_err_to_string(f: FixupError) -> String {
354359

355360
pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>,
356361
tables: &'a RefCell<ty::Tables<'tcx>>,
357-
param_env: Option<ty::ParameterEnvironment<'a, 'tcx>>)
362+
param_env: Option<ty::ParameterEnvironment<'a, 'tcx>>,
363+
projection_mode: ProjectionMode)
358364
-> InferCtxt<'a, 'tcx> {
359365
InferCtxt {
360366
tcx: tcx,
@@ -366,14 +372,16 @@ pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>,
366372
parameter_environment: param_env.unwrap_or(tcx.empty_parameter_environment()),
367373
reported_trait_errors: RefCell::new(FnvHashSet()),
368374
normalize: false,
375+
projection_mode: projection_mode,
369376
err_count_on_creation: tcx.sess.err_count()
370377
}
371378
}
372379

373380
pub fn normalizing_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>,
374-
tables: &'a RefCell<ty::Tables<'tcx>>)
381+
tables: &'a RefCell<ty::Tables<'tcx>>,
382+
projection_mode: ProjectionMode)
375383
-> InferCtxt<'a, 'tcx> {
376-
let mut infcx = new_infer_ctxt(tcx, tables, None);
384+
let mut infcx = new_infer_ctxt(tcx, tables, None, projection_mode);
377385
infcx.normalize = true;
378386
infcx
379387
}
@@ -514,6 +522,7 @@ pub struct CombinedSnapshot {
514522
region_vars_snapshot: RegionSnapshot,
515523
}
516524

525+
// NOTE: Callable from trans only!
517526
pub fn normalize_associated_type<'tcx,T>(tcx: &TyCtxt<'tcx>, value: &T) -> T
518527
where T : TypeFoldable<'tcx>
519528
{
@@ -525,7 +534,7 @@ pub fn normalize_associated_type<'tcx,T>(tcx: &TyCtxt<'tcx>, value: &T) -> T
525534
return value;
526535
}
527536

528-
let infcx = new_infer_ctxt(tcx, &tcx.tables, None);
537+
let infcx = new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::Any);
529538
let mut selcx = traits::SelectionContext::new(&infcx);
530539
let cause = traits::ObligationCause::dummy();
531540
let traits::Normalized { value: result, obligations } =
@@ -593,6 +602,10 @@ pub fn drain_fulfillment_cx<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
593602
}
594603

595604
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
605+
pub fn projection_mode(&self) -> ProjectionMode {
606+
self.projection_mode
607+
}
608+
596609
pub fn freshen<T:TypeFoldable<'tcx>>(&self, t: T) -> T {
597610
t.fold_with(&mut self.freshener())
598611
}

src/librustc/middle/traits/coherence.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
//! See `README.md` for high-level documentation
1212
13-
use super::build_selcx;
1413
use super::{SelectionContext, Obligation, ObligationCause};
1514
use super::util;
1615

@@ -37,7 +36,7 @@ pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>,
3736
impl1_def_id,
3837
impl2_def_id);
3938

40-
let selcx = &mut build_selcx(infcx).project_topmost().intercrate().build();
39+
let selcx = &mut SelectionContext::intercrate(infcx);
4140
overlap(selcx, impl1_def_id, impl2_def_id)
4241
}
4342

src/librustc/middle/traits/mod.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,15 @@ pub use self::coherence::orphan_check;
3636
pub use self::coherence::overlapping_impls;
3737
pub use self::coherence::OrphanCheckErr;
3838
pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
39-
pub use self::project::MismatchedProjectionTypes;
40-
pub use self::project::normalize;
41-
pub use self::project::Normalized;
39+
pub use self::project::{MismatchedProjectionTypes, ProjectionMode};
40+
pub use self::project::{normalize, Normalized};
4241
pub use self::object_safety::is_object_safe;
4342
pub use self::object_safety::astconv_object_safety_violations;
4443
pub use self::object_safety::object_safety_violations;
4544
pub use self::object_safety::ObjectSafetyViolation;
4645
pub use self::object_safety::MethodViolationCode;
4746
pub use self::object_safety::is_vtable_safe_method;
48-
pub use self::select::{EvaluationCache, SelectionContextBuilder, build_selcx};
49-
pub use self::select::{ProjectionMode, SelectionContext, SelectionCache};
47+
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
5048
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
5149
pub use self::select::{MethodMatchedData}; // intentionally don't export variants
5250
pub use self::specialize::{Overlap, specialization_graph, specializes, translate_substs};
@@ -435,7 +433,7 @@ pub fn normalize_param_env_or_error<'a,'tcx>(unnormalized_env: ty::ParameterEnvi
435433

436434
let elaborated_env = unnormalized_env.with_caller_bounds(predicates);
437435

438-
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(elaborated_env));
436+
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(elaborated_env), ProjectionMode::AnyFinal);
439437
let predicates = match fully_normalize(&infcx,
440438
cause,
441439
&infcx.parameter_environment.caller_bounds) {

src/librustc/middle/traits/project.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,94 @@ use util::common::FN_OUTPUT_NAME;
3535

3636
use std::rc::Rc;
3737

38+
/// Depending on the stage of compilation, we want projection to be
39+
/// more or less conservative.
40+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
41+
pub enum ProjectionMode {
42+
/// At coherence-checking time, we're still constructing the
43+
/// specialization graph, and thus we only project project
44+
/// non-`default` associated types that are defined directly in
45+
/// the applicable impl. (This behavior should be improved over
46+
/// time, to allow for successful projections modulo cycles
47+
/// between different impls).
48+
// TODO: Add tracking issue to do better here.
49+
///
50+
/// Here's an example that will fail due to the restriction:
51+
///
52+
/// ```
53+
/// trait Assoc {
54+
/// type Output;
55+
/// }
56+
///
57+
/// impl<T> Assoc for T {
58+
/// type Output = bool;
59+
/// }
60+
///
61+
/// impl Assoc for u8 {} // <- inherits the non-default type from above
62+
///
63+
/// trait Foo {}
64+
/// impl Foo for u32 {}
65+
/// impl Foo for <u8 as Assoc>::Output {} // <- this projection will fail
66+
/// ```
67+
///
68+
/// The projection would succeed if `Output` had been defined
69+
/// directly in the impl for `u8`.
70+
// TODO: Add test
71+
Topmost,
72+
73+
/// At type-checking time, we refuse to project any associated
74+
/// type that is marked `default`. Non-`default` ("final") types
75+
/// are always projected. This is necessary in general for
76+
/// soundness of specialization. However, we *could* allow
77+
/// projections in fully-monomorphic cases. We choose not to,
78+
/// because we prefer for `default type` to force the type
79+
/// definition to be treated abstractly by any consumers of the
80+
/// impl. Concretely, that means that the following example will
81+
/// fail to compile:
82+
///
83+
/// ```
84+
/// trait Assoc {
85+
/// type Output;
86+
/// }
87+
///
88+
/// impl<T> Assoc for T {
89+
/// default type Output = bool;
90+
/// }
91+
///
92+
/// fn main() {
93+
/// let <() as Assoc>::Output = true;
94+
/// }
95+
// TODO: Add test
96+
AnyFinal,
97+
98+
/// At trans time, all projections will succeed.
99+
Any,
100+
}
101+
102+
impl ProjectionMode {
103+
pub fn topmost(&self) -> bool {
104+
match *self {
105+
ProjectionMode::Topmost => true,
106+
_ => false,
107+
}
108+
}
109+
110+
pub fn any_final(&self) -> bool {
111+
match *self {
112+
ProjectionMode::AnyFinal => true,
113+
_ => false,
114+
}
115+
}
116+
117+
pub fn any(&self) -> bool {
118+
match *self {
119+
ProjectionMode::Any => true,
120+
_ => false,
121+
}
122+
}
123+
}
124+
125+
38126
pub type PolyProjectionObligation<'tcx> =
39127
Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>;
40128

0 commit comments

Comments
 (0)