Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement RFC 1210: impl specialization #30652

Merged
merged 37 commits into from
Mar 15, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
7366034
Fix existing comment typo.
aturon Dec 4, 2015
659ba09
Remove useless vector accumulation in `type_vars_for_defs`
aturon Dec 8, 2015
8fe63e2
Add `default` as contextual keyword, and parse it for impl items.
aturon Dec 18, 2015
991f32a
Hook `default` keyword into metadata and carry data through to typeck
aturon Dec 29, 2015
45f4bf1
Refactor `impl_trait_ref_and_oblig`, making it generally available as…
aturon Dec 30, 2015
c5849e4
Add specialization module.
aturon Dec 22, 2015
1f34086
Initial incorporation of specialization:
aturon Dec 22, 2015
957ee5c
Add subst helper for inheriting FnSpace from another subst
aturon Dec 28, 2015
7e42a78
Implement default method inheritance.
aturon Dec 28, 2015
1077ff2
Add basic specialization tests, including for default item
aturon Dec 28, 2015
5dedbda
Check for proper use of `default` keyword in specializing impls.
aturon Dec 29, 2015
b7e5112
Implement default associated type inheritance.
aturon Dec 29, 2015
a382582
Add tests for specialization on associated types
aturon Dec 29, 2015
d816079
Adjust overlap-related tests to account for cosmetic changes to error…
aturon Dec 30, 2015
7976e36
Adjust test for new overlap message on default trait impls
aturon Dec 30, 2015
9f16c2c
Adjust coherence test to reflect that only the orphan rule prevents y…
aturon Dec 30, 2015
e816910
Add feature gate
aturon Dec 30, 2015
ed8d059
Adjust tests for feature gate, and add tests for the gate itself
aturon Dec 30, 2015
c1df41e
Add some basic comments about how specialization fits into the rest o…
aturon Dec 30, 2015
9734406
Assorted fixed after rebasing
aturon Jan 30, 2016
462c83e
Address basic nits from initial review
aturon Jan 30, 2016
940adda
Move specialization graph walks to iterators; make associated type
aturon Feb 16, 2016
8f20cbf
Add more commentary for subst translation
aturon Feb 17, 2016
eaf2f90
Refactor core specialization and subst translation code to avoid
aturon Feb 19, 2016
1726c1b
Add some debugging output for specialization graph assembly
aturon Feb 23, 2016
2651f8c
Add regression tests from initial implementation review
aturon Feb 23, 2016
386f8ee
Forbid cross-polarity specializations
aturon Feb 23, 2016
9bcfdb7
Move projection_mode to InferContext rather than SelectionContext to …
aturon Feb 23, 2016
8f0e73e
Address review comments
aturon Mar 8, 2016
35437c7
Fixes after a rebase
aturon Mar 9, 2016
3262016
Move tests to dedicated subdirectories
aturon Mar 9, 2016
d80189d
Test fixes, added README for tests
aturon Mar 11, 2016
e36620d
Introduce ICE when the topmost projection restriction kicks in, as pe…
aturon Mar 11, 2016
e5753b4
Fixes after rebase
aturon Mar 11, 2016
c4f78ad
Parse fail test fixes
aturon Mar 13, 2016
dc45d92
Adjust error code
aturon Mar 13, 2016
6562eeb
Add pretty printer output for `default`
aturon Mar 14, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/librustc/dep_graph/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ could invalidate work done for other items. So, for example:
not shared state, because if it changes it does not itself
invalidate other functions (though it may be that it causes new
monomorphizations to occur, but that's handled independently).

Put another way: if the HIR for an item changes, we are going to
recompile that item for sure. But we need the dep tracking map to tell
us what *else* we have to recompile. Shared state is anything that is
Expand Down Expand Up @@ -177,7 +177,7 @@ reads from `item`, there would be missing edges in the graph:
| ^
| |
+---------------------------------+ // added by `visit_all_items_in_krate`

In particular, the edge from `Hir(X)` to `ItemSignature(X)` is only
present because we called `read` ourselves when entering the `ItemSignature(X)`
task.
Expand Down Expand Up @@ -273,8 +273,8 @@ should not exist. In contrast, using the memoized helper, you get:
... -> MapVariant(key) -> A
|
+----------> B
which is much cleaner.

which is much cleaner.

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

7 changes: 5 additions & 2 deletions src/librustc/middle/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use middle::expr_use_visitor as euv;
use middle::infer;
use middle::mem_categorization::{cmt};
use middle::pat_util::*;
use middle::traits::ProjectionMode;
use middle::ty::*;
use middle::ty;
use std::cmp::Ordering;
Expand Down Expand Up @@ -1101,7 +1102,8 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
//FIXME: (@jroesch) this code should be floated up as well
let infcx = infer::new_infer_ctxt(cx.tcx,
&cx.tcx.tables,
Some(cx.param_env.clone()));
Some(cx.param_env.clone()),
ProjectionMode::AnyFinal);
if infcx.type_moves_by_default(pat_ty, pat.span) {
check_move(p, sub.as_ref().map(|p| &**p));
}
Expand Down Expand Up @@ -1133,7 +1135,8 @@ fn check_for_mutation_in_guard<'a, 'tcx>(cx: &'a MatchCheckCtxt<'a, 'tcx>,

let infcx = infer::new_infer_ctxt(cx.tcx,
&cx.tcx.tables,
Some(checker.cx.param_env.clone()));
Some(checker.cx.param_env.clone()),
ProjectionMode::AnyFinal);

let mut visitor = ExprUseVisitor::new(&mut checker, &infcx);
visitor.walk_expr(guard);
Expand Down
8 changes: 7 additions & 1 deletion src/librustc/middle/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use middle::def_id::DefId;
use middle::pat_util::def_to_path;
use middle::ty::{self, Ty, TyCtxt};
use middle::ty::util::IntTypeExt;
use middle::traits::ProjectionMode;
use middle::astconv_util::ast_ty_to_prim_ty;
use util::nodemap::NodeMap;

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

tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id());
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal);

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

// NOTE: this code does not currently account for specialization, but when
// it does so, it should hook into the ProjectionMode to determine when the
// constant should resolve; this will also require plumbing through to this
// function whether we are in "trans mode" to pick the right ProjectionMode
// when constructing the inference context above.
match selection {
traits::VtableImpl(ref impl_data) => {
match tcx.associated_consts(impl_data.impl_def_id)
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/middle/cstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ pub trait CrateStore<'tcx> : Any {
-> Option<ty::adjustment::CustomCoerceUnsized>;
fn associated_consts(&self, tcx: &TyCtxt<'tcx>, def: DefId)
-> Vec<Rc<ty::AssociatedConst<'tcx>>>;
fn impl_parent(&self, impl_def_id: DefId) -> Option<DefId>;

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

// trait/impl-item info
fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId)
Expand Down
26 changes: 18 additions & 8 deletions src/librustc/middle/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use middle::region::CodeExtent;
use middle::subst;
use middle::subst::Substs;
use middle::subst::Subst;
use middle::traits;
use middle::traits::{self, ProjectionMode};
use middle::ty::adjustment;
use middle::ty::{TyVid, IntVid, FloatVid};
use middle::ty::{self, Ty, TyCtxt};
Expand Down Expand Up @@ -99,6 +99,11 @@ pub struct InferCtxt<'a, 'tcx: 'a> {
// directly.
normalize: bool,

// Sadly, the behavior of projection varies a bit depending on the
// stage of compilation. The specifics are given in the
// documentation for `ProjectionMode`.
projection_mode: ProjectionMode,

err_count_on_creation: usize,
}

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

pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>,
tables: &'a RefCell<ty::Tables<'tcx>>,
param_env: Option<ty::ParameterEnvironment<'a, 'tcx>>)
param_env: Option<ty::ParameterEnvironment<'a, 'tcx>>,
projection_mode: ProjectionMode)
-> InferCtxt<'a, 'tcx> {
InferCtxt {
tcx: tcx,
Expand All @@ -366,14 +372,16 @@ pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>,
parameter_environment: param_env.unwrap_or(tcx.empty_parameter_environment()),
reported_trait_errors: RefCell::new(FnvHashSet()),
normalize: false,
projection_mode: projection_mode,
err_count_on_creation: tcx.sess.err_count()
}
}

pub fn normalizing_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>,
tables: &'a RefCell<ty::Tables<'tcx>>)
tables: &'a RefCell<ty::Tables<'tcx>>,
projection_mode: ProjectionMode)
-> InferCtxt<'a, 'tcx> {
let mut infcx = new_infer_ctxt(tcx, tables, None);
let mut infcx = new_infer_ctxt(tcx, tables, None, projection_mode);
infcx.normalize = true;
infcx
}
Expand Down Expand Up @@ -514,6 +522,7 @@ pub struct CombinedSnapshot {
region_vars_snapshot: RegionSnapshot,
}

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

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

impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
pub fn projection_mode(&self) -> ProjectionMode {
self.projection_mode
}

pub fn freshen<T:TypeFoldable<'tcx>>(&self, t: T) -> T {
t.fold_with(&mut self.freshener())
}
Expand Down Expand Up @@ -1025,8 +1038,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
substs: &mut Substs<'tcx>,
defs: &[ty::TypeParameterDef<'tcx>]) {

let mut vars = Vec::with_capacity(defs.len());

for def in defs.iter() {
let default = def.default.map(|default| {
type_variable::Default {
Expand All @@ -1038,7 +1049,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {

let ty_var = self.next_ty_var_with_default(default);
substs.types.push(space, ty_var);
vars.push(ty_var)
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/librustc/middle/subst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,15 @@ impl<'tcx> Substs<'tcx> {
Substs { types: types, regions: regions }
}

pub fn with_method_from_subst(self, other: &Substs<'tcx>) -> Substs<'tcx> {
let Substs { types, regions } = self;
let types = types.with_slice(FnSpace, other.types.get_slice(FnSpace));
let regions = regions.map(|r| {
r.with_slice(FnSpace, other.regions().get_slice(FnSpace))
});
Substs { types: types, regions: regions }
}

/// Creates a trait-ref out of this substs, ignoring the FnSpace substs
pub fn to_trait_ref(&self, tcx: &TyCtxt<'tcx>, trait_id: DefId)
-> ty::TraitRef<'tcx> {
Expand Down
40 changes: 40 additions & 0 deletions src/librustc/middle/traits/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,3 +428,43 @@ We used to try and draw finer-grained distinctions, but that led to a
serious of annoying and weird bugs like #22019 and #18290. This simple
rule seems to be pretty clearly safe and also still retains a very
high hit rate (~95% when compiling rustc).

# Specialization

Defined in the `specialize` module.

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.
7 changes: 3 additions & 4 deletions src/librustc/middle/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@

//! See `README.md` for high-level documentation

use super::{SelectionContext};
use super::{Obligation, ObligationCause};
use super::{SelectionContext, Obligation, ObligationCause};

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

/// If there are types that satisfy both impls, returns an `ImplTy`
/// with those types substituted (by updating the given `infcx`)
/// If there are types that satisfy both impls, returns a suitably-freshened
/// `ImplHeader` with those types substituted
pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>,
impl1_def_id: DefId,
impl2_def_id: DefId)
Expand Down
16 changes: 9 additions & 7 deletions src/librustc/middle/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,18 @@ pub use self::coherence::orphan_check;
pub use self::coherence::overlapping_impls;
pub use self::coherence::OrphanCheckErr;
pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
pub use self::project::MismatchedProjectionTypes;
pub use self::project::normalize;
pub use self::project::Normalized;
pub use self::project::{MismatchedProjectionTypes, ProjectionMode};
pub use self::project::{normalize, Normalized};
pub use self::object_safety::is_object_safe;
pub use self::object_safety::astconv_object_safety_violations;
pub use self::object_safety::object_safety_violations;
pub use self::object_safety::ObjectSafetyViolation;
pub use self::object_safety::MethodViolationCode;
pub use self::object_safety::is_vtable_safe_method;
pub use self::select::EvaluationCache;
pub use self::select::SelectionContext;
pub use self::select::SelectionCache;
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
pub use self::select::{MethodMatchedData}; // intentionally don't export variants
pub use self::specialize::{Overlap, specialization_graph, specializes, translate_substs};
pub use self::util::elaborate_predicates;
pub use self::util::get_vtable_index_of_object_method;
pub use self::util::trait_ref_for_builtin_bound;
Expand All @@ -67,6 +65,7 @@ mod fulfill;
mod project;
mod object_safety;
mod select;
mod specialize;
mod structural_impls;
mod util;

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

let elaborated_env = unnormalized_env.with_caller_bounds(predicates);

let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(elaborated_env));
let infcx = infer::new_infer_ctxt(tcx,
&tcx.tables,
Some(elaborated_env),
ProjectionMode::AnyFinal);
let predicates = match fully_normalize(&infcx,
cause,
&infcx.parameter_environment.caller_bounds) {
Expand Down
Loading