Skip to content

Commit 9ca7561

Browse files
committed
Auto merge of #30652 - aturon:specialization, r=nikomatsakis
Implement RFC 1210: impl specialization This PR implements [impl specialization](rust-lang/rfcs#1210), carefully following the proposal laid out in the RFC. The implementation covers the bulk of the RFC. The remaining gaps I know of are: - no checking for lifetime-dependent specialization (a soundness hole); - no `default impl` yet; - no support for `default` with associated consts; I plan to cover these gaps in follow-up PRs, as per @nikomatsakis's preference. The basic strategy is to build up a *specialization graph* during coherence checking. Insertion into the graph locates the right place to put an impl in the specialization hierarchy; if there is no right place (due to partial overlap but no containment), you get an overlap error. Specialization is consulted when selecting an impl (of course), and the graph is consulted when propagating defaults down the specialization hierarchy. You might expect that the specialization graph would be used during selection -- i.e., when actually performing specialization. This is not done for two reasons: - It's merely an optimization: given a set of candidates that apply, we can determine the most specialized one by comparing them directly for specialization, rather than consulting the graph. Given that we also cache the results of selection, the benefit of this optimization is questionable. - To build the specialization graph in the first place, we need to use selection (because we need to determine whether one impl specializes another). Dealing with this reentrancy would require some additional mode switch for selection. Given that there seems to be no strong reason to use the graph anyway, we stick with a simpler approach in selection, and use the graph only for propagating default implementations. Trait impl selection can succeed even when multiple impls can apply, as long as they are part of the same specialization family. In that case, it returns a *single* impl on success -- this is the most specialized impl *known* to apply. However, if there are any inference variables in play, the returned impl may not be the actual impl we will use at trans time. Thus, we take special care to avoid projecting associated types unless either (1) the associated type does not use `default` and thus cannot be overridden or (2) all input types are known concretely. r? @nikomatsakis
2 parents 6d215fe + 6562eeb commit 9ca7561

File tree

115 files changed

+3029
-620
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

115 files changed

+3029
-620
lines changed

Diff for: src/librustc/dep_graph/README.md

+4-5
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ could invalidate work done for other items. So, for example:
5151
not shared state, because if it changes it does not itself
5252
invalidate other functions (though it may be that it causes new
5353
monomorphizations to occur, but that's handled independently).
54-
54+
5555
Put another way: if the HIR for an item changes, we are going to
5656
recompile that item for sure. But we need the dep tracking map to tell
5757
us what *else* we have to recompile. Shared state is anything that is
@@ -177,7 +177,7 @@ reads from `item`, there would be missing edges in the graph:
177177
| ^
178178
| |
179179
+---------------------------------+ // added by `visit_all_items_in_krate`
180-
180+
181181
In particular, the edge from `Hir(X)` to `ItemSignature(X)` is only
182182
present because we called `read` ourselves when entering the `ItemSignature(X)`
183183
task.
@@ -273,8 +273,8 @@ should not exist. In contrast, using the memoized helper, you get:
273273
... -> MapVariant(key) -> A
274274
|
275275
+----------> B
276-
277-
which is much cleaner.
276+
277+
which is much cleaner.
278278

279279
**Be aware though that the closure is executed with `MapVariant(key)`
280280
pushed onto the stack as the current task!** That means that you must
@@ -387,4 +387,3 @@ RUST_DEP_GRAPH_FILTER='Hir&foo -> TypeckItemBody & bar'
387387
This will dump out all the nodes that lead from `Hir(foo)` to
388388
`TypeckItemBody(bar)`, from which you can (hopefully) see the source
389389
of the erroneous edge.
390-

Diff for: src/librustc/middle/check_match.rs

+5-2
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);

Diff for: src/librustc/middle/const_eval.rs

+7-1
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)

Diff for: src/librustc/middle/cstore.rs

+2
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ pub trait CrateStore<'tcx> : Any {
176176
-> Option<ty::adjustment::CustomCoerceUnsized>;
177177
fn associated_consts(&self, tcx: &TyCtxt<'tcx>, def: DefId)
178178
-> Vec<Rc<ty::AssociatedConst<'tcx>>>;
179+
fn impl_parent(&self, impl_def_id: DefId) -> Option<DefId>;
179180

180181
// trait/impl-item info
181182
fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId)
@@ -346,6 +347,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
346347
{ unimplemented!() }
347348
fn associated_consts(&self, tcx: &TyCtxt<'tcx>, def: DefId)
348349
-> Vec<Rc<ty::AssociatedConst<'tcx>>> { unimplemented!() }
350+
fn impl_parent(&self, def: DefId) -> Option<DefId> { unimplemented!() }
349351

350352
// trait/impl-item info
351353
fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId)

Diff for: src/librustc/middle/infer/mod.rs

+18-8
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
}
@@ -1025,8 +1038,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
10251038
substs: &mut Substs<'tcx>,
10261039
defs: &[ty::TypeParameterDef<'tcx>]) {
10271040

1028-
let mut vars = Vec::with_capacity(defs.len());
1029-
10301041
for def in defs.iter() {
10311042
let default = def.default.map(|default| {
10321043
type_variable::Default {
@@ -1038,7 +1049,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
10381049

10391050
let ty_var = self.next_ty_var_with_default(default);
10401051
substs.types.push(space, ty_var);
1041-
vars.push(ty_var)
10421052
}
10431053
}
10441054

Diff for: src/librustc/middle/subst.rs

+9
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,15 @@ impl<'tcx> Substs<'tcx> {
160160
Substs { types: types, regions: regions }
161161
}
162162

163+
pub fn with_method_from_subst(self, other: &Substs<'tcx>) -> Substs<'tcx> {
164+
let Substs { types, regions } = self;
165+
let types = types.with_slice(FnSpace, other.types.get_slice(FnSpace));
166+
let regions = regions.map(|r| {
167+
r.with_slice(FnSpace, other.regions().get_slice(FnSpace))
168+
});
169+
Substs { types: types, regions: regions }
170+
}
171+
163172
/// Creates a trait-ref out of this substs, ignoring the FnSpace substs
164173
pub fn to_trait_ref(&self, tcx: &TyCtxt<'tcx>, trait_id: DefId)
165174
-> ty::TraitRef<'tcx> {

Diff for: src/librustc/middle/traits/README.md

+40
Original file line numberDiff line numberDiff line change
@@ -428,3 +428,43 @@ We used to try and draw finer-grained distinctions, but that led to a
428428
serious of annoying and weird bugs like #22019 and #18290. This simple
429429
rule seems to be pretty clearly safe and also still retains a very
430430
high hit rate (~95% when compiling rustc).
431+
432+
# Specialization
433+
434+
Defined in the `specialize` module.
435+
436+
The basic strategy is to build up a *specialization graph* during
437+
coherence checking. Insertion into the graph locates the right place
438+
to put an impl in the specialization hierarchy; if there is no right
439+
place (due to partial overlap but no containment), you get an overlap
440+
error. Specialization is consulted when selecting an impl (of course),
441+
and the graph is consulted when propagating defaults down the
442+
specialization hierarchy.
443+
444+
You might expect that the specialization graph would be used during
445+
selection -- i.e., when actually performing specialization. This is
446+
not done for two reasons:
447+
448+
- It's merely an optimization: given a set of candidates that apply,
449+
we can determine the most specialized one by comparing them directly
450+
for specialization, rather than consulting the graph. Given that we
451+
also cache the results of selection, the benefit of this
452+
optimization is questionable.
453+
454+
- To build the specialization graph in the first place, we need to use
455+
selection (because we need to determine whether one impl specializes
456+
another). Dealing with this reentrancy would require some additional
457+
mode switch for selection. Given that there seems to be no strong
458+
reason to use the graph anyway, we stick with a simpler approach in
459+
selection, and use the graph only for propagating default
460+
implementations.
461+
462+
Trait impl selection can succeed even when multiple impls can apply,
463+
as long as they are part of the same specialization family. In that
464+
case, it returns a *single* impl on success -- this is the most
465+
specialized impl *known* to apply. However, if there are any inference
466+
variables in play, the returned impl may not be the actual impl we
467+
will use at trans time. Thus, we take special care to avoid projecting
468+
associated types unless either (1) the associated type does not use
469+
`default` and thus cannot be overridden or (2) all input types are
470+
known concretely.

Diff for: src/librustc/middle/traits/coherence.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010

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

1615
use middle::cstore::LOCAL_CRATE;
1716
use middle::def_id::DefId;
@@ -23,8 +22,8 @@ use syntax::codemap::DUMMY_SP;
2322
#[derive(Copy, Clone)]
2423
struct InferIsLocal(bool);
2524

26-
/// If there are types that satisfy both impls, returns an `ImplTy`
27-
/// with those types substituted (by updating the given `infcx`)
25+
/// If there are types that satisfy both impls, returns a suitably-freshened
26+
/// `ImplHeader` with those types substituted
2827
pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>,
2928
impl1_def_id: DefId,
3029
impl2_def_id: DefId)

Diff for: src/librustc/middle/traits/mod.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,18 @@ 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;
49-
pub use self::select::SelectionContext;
50-
pub use self::select::SelectionCache;
47+
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
5148
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
5249
pub use self::select::{MethodMatchedData}; // intentionally don't export variants
50+
pub use self::specialize::{Overlap, specialization_graph, specializes, translate_substs};
5351
pub use self::util::elaborate_predicates;
5452
pub use self::util::get_vtable_index_of_object_method;
5553
pub use self::util::trait_ref_for_builtin_bound;
@@ -67,6 +65,7 @@ mod fulfill;
6765
mod project;
6866
mod object_safety;
6967
mod select;
68+
mod specialize;
7069
mod structural_impls;
7170
mod util;
7271

@@ -434,7 +433,10 @@ pub fn normalize_param_env_or_error<'a,'tcx>(unnormalized_env: ty::ParameterEnvi
434433

435434
let elaborated_env = unnormalized_env.with_caller_bounds(predicates);
436435

437-
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(elaborated_env));
436+
let infcx = infer::new_infer_ctxt(tcx,
437+
&tcx.tables,
438+
Some(elaborated_env),
439+
ProjectionMode::AnyFinal);
438440
let predicates = match fully_normalize(&infcx,
439441
cause,
440442
&infcx.parameter_environment.caller_bounds) {

0 commit comments

Comments
 (0)