rustc_typeck: support functions in variance computation.

This commit is contained in:
Eduard-Mihai Burtescu 2017-06-02 22:05:41 +03:00
parent 33ecf72e8e
commit a9d4069975
9 changed files with 316 additions and 504 deletions

View File

@ -291,7 +291,7 @@ impl<'tcx> Relate<'tcx> for ty::TraitRef<'tcx> {
if a.def_id != b.def_id {
Err(TypeError::Traits(expected_found(relation, &a.def_id, &b.def_id)))
} else {
let substs = relation.relate_item_substs(a.def_id, a.substs, b.substs)?;
let substs = relate_substs(relation, None, a.substs, b.substs)?;
Ok(ty::TraitRef { def_id: a.def_id, substs: substs })
}
}
@ -308,7 +308,7 @@ impl<'tcx> Relate<'tcx> for ty::ExistentialTraitRef<'tcx> {
if a.def_id != b.def_id {
Err(TypeError::Traits(expected_found(relation, &a.def_id, &b.def_id)))
} else {
let substs = relation.relate_item_substs(a.def_id, a.substs, b.substs)?;
let substs = relate_substs(relation, None, a.substs, b.substs)?;
Ok(ty::ExistentialTraitRef { def_id: a.def_id, substs: substs })
}
}
@ -443,7 +443,7 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R,
(&ty::TyFnDef(a_def_id, a_substs), &ty::TyFnDef(b_def_id, b_substs))
if a_def_id == b_def_id =>
{
let substs = relate_substs(relation, None, a_substs, b_substs)?;
let substs = relation.relate_item_substs(a_def_id, a_substs, b_substs)?;
Ok(tcx.mk_fn_def(a_def_id, substs))
}

View File

@ -524,7 +524,11 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
ty: Some(self.encode_item_type(def_id)),
inherent_impls: LazySeq::empty(),
variances: LazySeq::empty(),
variances: if variant.ctor_kind == CtorKind::Fn {
self.encode_variances_of(def_id)
} else {
LazySeq::empty()
},
generics: Some(self.encode_generics(def_id)),
predicates: Some(self.encode_predicates(def_id)),
@ -652,7 +656,11 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
ty: Some(self.encode_item_type(def_id)),
inherent_impls: LazySeq::empty(),
variances: LazySeq::empty(),
variances: if variant.ctor_kind == CtorKind::Fn {
self.encode_variances_of(def_id)
} else {
LazySeq::empty()
},
generics: Some(self.encode_generics(def_id)),
predicates: Some(self.encode_predicates(def_id)),
@ -744,7 +752,11 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
}
},
inherent_impls: LazySeq::empty(),
variances: LazySeq::empty(),
variances: if trait_item.kind == ty::AssociatedKind::Method {
self.encode_variances_of(def_id)
} else {
LazySeq::empty()
},
generics: Some(self.encode_generics(def_id)),
predicates: Some(self.encode_predicates(def_id)),
@ -821,7 +833,11 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
ty: Some(self.encode_item_type(def_id)),
inherent_impls: LazySeq::empty(),
variances: LazySeq::empty(),
variances: if impl_item.kind == ty::AssociatedKind::Method {
self.encode_variances_of(def_id)
} else {
LazySeq::empty()
},
generics: Some(self.encode_generics(def_id)),
predicates: Some(self.encode_predicates(def_id)),
@ -1055,7 +1071,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
hir::ItemEnum(..) |
hir::ItemStruct(..) |
hir::ItemUnion(..) |
hir::ItemTrait(..) => self.encode_variances_of(def_id),
hir::ItemFn(..) => self.encode_variances_of(def_id),
_ => LazySeq::empty(),
},
generics: match item.node {
@ -1400,7 +1416,10 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
ty: Some(self.encode_item_type(def_id)),
inherent_impls: LazySeq::empty(),
variances: LazySeq::empty(),
variances: match nitem.node {
hir::ForeignItemFn(..) => self.encode_variances_of(def_id),
_ => LazySeq::empty(),
},
generics: Some(self.encode_generics(def_id)),
predicates: Some(self.encode_predicates(def_id)),

View File

@ -14,11 +14,9 @@
//! We walk the set of items and, for each member, generate new constraints.
use hir::def_id::DefId;
use middle::resolve_lifetime as rl;
use rustc::dep_graph::{AssertDepGraphSafe, DepKind};
use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::hir::map as hir_map;
use syntax::ast;
use rustc::hir;
use rustc::hir::itemlikevisit::ItemLikeVisitor;
@ -61,10 +59,10 @@ pub struct Constraint<'a> {
/// }
///
/// then while we are visiting `Bar<T>`, the `CurrentItem` would have
/// the def-id and generics of `Foo`.
pub struct CurrentItem<'a> {
/// the def-id and the start of `Foo`'s inferreds.
pub struct CurrentItem {
def_id: DefId,
generics: &'a ty::Generics,
inferred_start: InferredIndex,
}
pub fn add_constraints_from_crate<'a, 'tcx>(terms_cx: TermsContext<'a, 'tcx>)
@ -91,8 +89,59 @@ pub fn add_constraints_from_crate<'a, 'tcx>(terms_cx: TermsContext<'a, 'tcx>)
impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> {
fn visit_item(&mut self, item: &hir::Item) {
match item.node {
hir::ItemStruct(ref struct_def, _) |
hir::ItemUnion(ref struct_def, _) => {
self.visit_node_helper(item.id);
if let hir::VariantData::Tuple(..) = *struct_def {
self.visit_node_helper(struct_def.id());
}
}
hir::ItemEnum(ref enum_def, _) => {
self.visit_node_helper(item.id);
for variant in &enum_def.variants {
if let hir::VariantData::Tuple(..) = variant.node.data {
self.visit_node_helper(variant.node.data.id());
}
}
}
hir::ItemFn(..) => {
self.visit_node_helper(item.id);
}
hir::ItemForeignMod(ref foreign_mod) => {
for foreign_item in &foreign_mod.items {
if let hir::ForeignItemFn(..) = foreign_item.node {
self.visit_node_helper(foreign_item.id);
}
}
}
_ => {}
}
}
fn visit_trait_item(&mut self, trait_item: &hir::TraitItem) {
if let hir::TraitItemKind::Method(..) = trait_item.node {
self.visit_node_helper(trait_item.id);
}
}
fn visit_impl_item(&mut self, impl_item: &hir::ImplItem) {
if let hir::ImplItemKind::Method(..) = impl_item.node {
self.visit_node_helper(impl_item.id);
}
}
}
impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
fn visit_node_helper(&mut self, id: ast::NodeId) {
let tcx = self.terms_cx.tcx;
let def_id = tcx.hir.local_def_id(item.id);
let def_id = tcx.hir.local_def_id(id);
// Encapsulate constructing the constraints into a task we can
// reference later. This can go away once the red-green
@ -100,20 +149,11 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> {
//
// See README.md for a detailed discussion
// on dep-graph management.
match item.node {
hir::ItemEnum(..) |
hir::ItemStruct(..) |
hir::ItemUnion(..) => {
let dep_node = def_id.to_dep_node(tcx, DepKind::ItemVarianceConstraints);
tcx.dep_graph.with_task(dep_node,
AssertDepGraphSafe(self),
def_id,
visit_item_task);
}
_ => {
// Nothing to do here, skip the task.
}
}
let dep_node = def_id.to_dep_node(tcx, DepKind::ItemVarianceConstraints);
tcx.dep_graph.with_task(dep_node,
AssertDepGraphSafe(self),
def_id,
visit_item_task);
fn visit_item_task<'a, 'tcx>(ccx: AssertDepGraphSafe<&mut ConstraintContext<'a, 'tcx>>,
def_id: DefId)
@ -122,197 +162,57 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> {
}
}
fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) {
}
fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
}
}
/// Is `param_id` a lifetime according to `map`?
fn is_lifetime(map: &hir_map::Map, param_id: ast::NodeId) -> bool {
match map.find(param_id) {
Some(hir_map::NodeLifetime(..)) => true,
_ => false,
}
}
impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> {
self.terms_cx.tcx
}
fn build_constraints_for_item(&mut self, def_id: DefId) {
let tcx = self.tcx();
let id = self.tcx().hir.as_local_node_id(def_id).unwrap();
let item = tcx.hir.expect_item(id);
debug!("visit_item item={}", tcx.hir.node_to_string(item.id));
debug!("build_constraints_for_item({})", tcx.item_path_str(def_id));
match item.node {
hir::ItemEnum(..) |
hir::ItemStruct(..) |
hir::ItemUnion(..) => {
let generics = tcx.generics_of(def_id);
let current_item = &CurrentItem { def_id, generics };
// Skip items with no generics - there's nothing to infer in them.
if tcx.generics_of(def_id).count() == 0 {
return;
}
let id = tcx.hir.as_local_node_id(def_id).unwrap();
let inferred_start = self.terms_cx.inferred_starts[&id];
let current_item = &CurrentItem { def_id, inferred_start };
match tcx.type_of(def_id).sty {
ty::TyAdt(def, _) => {
// Not entirely obvious: constraints on structs/enums do not
// affect the variance of their type parameters. See discussion
// in comment at top of module.
//
// self.add_constraints_from_generics(generics);
for field in tcx.adt_def(def_id).all_fields() {
for field in def.all_fields() {
self.add_constraints_from_ty(current_item,
tcx.type_of(field.did),
self.covariant);
}
}
hir::ItemTrait(..) |
hir::ItemExternCrate(_) |
hir::ItemUse(..) |
hir::ItemStatic(..) |
hir::ItemConst(..) |
hir::ItemFn(..) |
hir::ItemMod(..) |
hir::ItemForeignMod(..) |
hir::ItemGlobalAsm(..) |
hir::ItemTy(..) |
hir::ItemImpl(..) |
hir::ItemDefaultImpl(..) => {
span_bug!(item.span, "`build_constraints_for_item` invoked for non-type-def");
}
}
}
/// Load the generics for another item, adding a corresponding
/// relation into the dependencies to indicate that the variance
/// for `current` relies on `def_id`.
fn read_generics(&mut self, current: &CurrentItem, def_id: DefId) -> &'tcx ty::Generics {
let generics = self.tcx().generics_of(def_id);
if self.tcx().dep_graph.is_fully_enabled() {
self.dependencies.add(current.def_id, def_id);
}
generics
}
fn opt_inferred_index(&self, param_id: ast::NodeId) -> Option<&InferredIndex> {
self.terms_cx.inferred_map.get(&param_id)
}
fn find_binding_for_lifetime(&self, param_id: ast::NodeId) -> ast::NodeId {
let tcx = self.terms_cx.tcx;
assert!(is_lifetime(&tcx.hir, param_id));
match tcx.named_region_map.defs.get(&param_id) {
Some(&rl::Region::EarlyBound(_, lifetime_decl_id)) => lifetime_decl_id,
Some(_) => bug!("should not encounter non early-bound cases"),
// The lookup should only fail when `param_id` is
// itself a lifetime binding: use it as the decl_id.
None => param_id,
}
}
/// Is `param_id` a type parameter for which we infer variance?
fn is_to_be_inferred(&self, param_id: ast::NodeId) -> bool {
let result = self.terms_cx.inferred_map.contains_key(&param_id);
// To safe-guard against invalid inferred_map constructions,
// double-check if variance is inferred at some use of a type
// parameter (by inspecting parent of its binding declaration
// to see if it is introduced by a type or by a fn/impl).
let check_result = |this: &ConstraintContext| -> bool {
let tcx = this.terms_cx.tcx;
let decl_id = this.find_binding_for_lifetime(param_id);
// Currently only called on lifetimes; double-checking that.
assert!(is_lifetime(&tcx.hir, param_id));
let parent_id = tcx.hir.get_parent(decl_id);
let parent = tcx.hir
.find(parent_id)
.unwrap_or_else(|| bug!("tcx.hir missing entry for id: {}", parent_id));
let is_inferred;
macro_rules! cannot_happen { () => { {
bug!("invalid parent: {} for {}",
tcx.hir.node_to_string(parent_id),
tcx.hir.node_to_string(param_id));
} } }
match parent {
hir_map::NodeItem(p) => {
match p.node {
hir::ItemTy(..) |
hir::ItemEnum(..) |
hir::ItemStruct(..) |
hir::ItemUnion(..) |
hir::ItemTrait(..) => is_inferred = true,
hir::ItemFn(..) => is_inferred = false,
_ => cannot_happen!(),
}
}
hir_map::NodeTraitItem(..) => is_inferred = false,
hir_map::NodeImplItem(..) => is_inferred = false,
_ => cannot_happen!(),
ty::TyFnDef(..) => {
self.add_constraints_from_sig(current_item,
tcx.fn_sig(def_id),
self.covariant);
}
return is_inferred;
};
assert_eq!(result, check_result(self));
return result;
}
/// Returns a variance term representing the declared variance of the type/region parameter
/// with the given id.
fn declared_variance(&self,
param_def_id: DefId,
item_def_id: DefId,
index: usize)
-> VarianceTermPtr<'a> {
assert_eq!(param_def_id.krate, item_def_id.krate);
if let Some(param_node_id) = self.tcx().hir.as_local_node_id(param_def_id) {
// Parameter on an item defined within current crate:
// variance not yet inferred, so return a symbolic
// variance.
if let Some(&InferredIndex(index)) = self.opt_inferred_index(param_node_id) {
self.terms_cx.inferred_infos[index].term
} else {
// If there is no inferred entry for a type parameter,
// it must be declared on a (locally defiend) trait -- they don't
// get inferreds because they are always invariant.
if cfg!(debug_assertions) {
let item_node_id = self.tcx().hir.as_local_node_id(item_def_id).unwrap();
let item = self.tcx().hir.expect_item(item_node_id);
let success = match item.node {
hir::ItemTrait(..) => true,
_ => false,
};
if !success {
bug!("parameter {:?} has no inferred, but declared on non-trait: {:?}",
item_def_id,
item);
}
}
self.invariant
_ => {
span_bug!(tcx.def_span(def_id),
"`build_constraints_for_item` unsupported for this item");
}
} else {
// Parameter on an item defined within another crate:
// variance already inferred, just look it up.
let variances = self.tcx().variances_of(item_def_id);
self.constant_term(variances[index])
}
}
fn add_constraint(&mut self,
InferredIndex(index): InferredIndex,
current: &CurrentItem,
index: u32,
variance: VarianceTermPtr<'a>) {
debug!("add_constraint(index={}, variance={:?})", index, variance);
self.constraints.push(Constraint {
inferred: InferredIndex(index),
inferred: InferredIndex(current.inferred_start.0 + index as usize),
variance: variance,
});
}
@ -354,15 +254,26 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
debug!("add_constraints_from_trait_ref: trait_ref={:?} variance={:?}",
trait_ref,
variance);
self.add_constraints_from_invariant_substs(current, trait_ref.substs, variance);
}
let trait_generics = self.tcx().generics_of(trait_ref.def_id);
fn add_constraints_from_invariant_substs(&mut self,
current: &CurrentItem,
substs: &Substs<'tcx>,
variance: VarianceTermPtr<'a>) {
debug!("add_constraints_from_invariant_substs: substs={:?} variance={:?}",
substs,
variance);
self.add_constraints_from_substs(current,
trait_ref.def_id,
&trait_generics.types,
&trait_generics.regions,
trait_ref.substs,
variance);
// Trait are always invariant so we can take advantage of that.
let variance_i = self.invariant(variance);
for ty in substs.types() {
self.add_constraints_from_ty(current, ty, variance_i);
}
for region in substs.regions() {
self.add_constraints_from_region(current, region, variance_i);
}
}
/// Adds constraints appropriate for an instance of `ty` appearing
@ -383,8 +294,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
}
ty::TyFnDef(..) |
ty::TyClosure(..) |
ty::TyAnon(..) => {
ty::TyClosure(..) => {
bug!("Unexpected closure type in variance computation");
}
@ -410,26 +320,15 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
}
ty::TyAdt(def, substs) => {
let adt_generics = self.read_generics(current, def.did);
self.add_constraints_from_substs(current,
def.did,
&adt_generics.types,
&adt_generics.regions,
substs,
variance);
self.add_constraints_from_substs(current, def.did, substs, variance);
}
ty::TyProjection(ref data) => {
let trait_ref = &data.trait_ref;
let trait_generics = self.tcx().generics_of(trait_ref.def_id);
self.add_constraints_from_trait_ref(current, data.trait_ref, variance);
}
self.add_constraints_from_substs(current,
trait_ref.def_id,
&trait_generics.types,
&trait_generics.regions,
trait_ref.substs,
variance);
ty::TyAnon(_, substs) => {
self.add_constraints_from_invariant_substs(current, substs, variance);
}
ty::TyDynamic(ref data, r) => {
@ -448,23 +347,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
}
ty::TyParam(ref data) => {
assert_eq!(current.generics.parent, None);
let mut i = data.idx as usize;
if !current.generics.has_self || i > 0 {
i -= current.generics.regions.len();
}
let def_id = current.generics.types[i].def_id;
let node_id = self.tcx().hir.as_local_node_id(def_id).unwrap();
match self.terms_cx.inferred_map.get(&node_id) {
Some(&index) => {
self.add_constraint(index, variance);
}
None => {
// We do not infer variance for type parameters
// declared on methods. They will not be present
// in the inferred_map.
}
}
self.add_constraint(current, data.idx, variance);
}
ty::TyFnPtr(sig) => {
@ -489,8 +372,6 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
fn add_constraints_from_substs(&mut self,
current: &CurrentItem,
def_id: DefId,
type_param_defs: &[ty::TypeParameterDef],
region_param_defs: &[ty::RegionParameterDef],
substs: &Substs<'tcx>,
variance: VarianceTermPtr<'a>) {
debug!("add_constraints_from_substs(def_id={:?}, substs={:?}, variance={:?})",
@ -498,21 +379,45 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
substs,
variance);
for p in type_param_defs {
let variance_decl = self.declared_variance(p.def_id, def_id, p.index as usize);
// We don't record `inferred_starts` entries for empty generics.
if substs.is_empty() {
return;
}
// Add a corresponding relation into the dependencies to
// indicate that the variance for `current` relies on `def_id`.
if self.tcx().dep_graph.is_fully_enabled() {
self.dependencies.add(current.def_id, def_id);
}
let (local, remote) = if let Some(id) = self.tcx().hir.as_local_node_id(def_id) {
(Some(self.terms_cx.inferred_starts[&id]), None)
} else {
(None, Some(self.tcx().variances_of(def_id)))
};
for (i, k) in substs.iter().enumerate() {
let variance_decl = if let Some(InferredIndex(start)) = local {
// Parameter on an item defined within current crate:
// variance not yet inferred, so return a symbolic
// variance.
self.terms_cx.inferred_terms[start + i]
} else {
// Parameter on an item defined within another crate:
// variance already inferred, just look it up.
self.constant_term(remote.as_ref().unwrap()[i])
};
let variance_i = self.xform(variance, variance_decl);
let substs_ty = substs.type_for_def(p);
debug!("add_constraints_from_substs: variance_decl={:?} variance_i={:?}",
variance_decl,
variance_i);
self.add_constraints_from_ty(current, substs_ty, variance_i);
}
for p in region_param_defs {
let variance_decl = self.declared_variance(p.def_id, def_id, p.index as usize);
let variance_i = self.xform(variance, variance_decl);
let substs_r = substs.region_for_def(p);
self.add_constraints_from_region(current, substs_r, variance_i);
if let Some(ty) = k.as_type() {
self.add_constraints_from_ty(current, ty, variance_i);
} else if let Some(r) = k.as_region() {
self.add_constraints_from_region(current, r, variance_i);
} else {
bug!();
}
}
}
@ -537,21 +442,14 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
variance: VarianceTermPtr<'a>) {
match *region {
ty::ReEarlyBound(ref data) => {
assert_eq!(current.generics.parent, None);
let i = data.index as usize - current.generics.has_self as usize;
let def_id = current.generics.regions[i].def_id;
let node_id = self.tcx().hir.as_local_node_id(def_id).unwrap();
if self.is_to_be_inferred(node_id) {
let &index = self.opt_inferred_index(node_id).unwrap();
self.add_constraint(index, variance);
}
self.add_constraint(current, data.index, variance);
}
ty::ReStatic => {}
ty::ReLateBound(..) => {
// We do not infer variance for region parameters on
// methods or in fn types.
// Late-bound regions do not get substituted the same
// way early-bound regions do, so we skip them here.
}
ty::ReFree(..) |

View File

@ -54,45 +54,63 @@ fn crate_variances<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_num: CrateNum)
fn variances_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_def_id: DefId)
-> Rc<Vec<ty::Variance>> {
let item_id = tcx.hir.as_local_node_id(item_def_id).expect("expected local def-id");
let item = tcx.hir.expect_item(item_id);
match item.node {
hir::ItemTrait(..) => {
// Traits are always invariant.
let generics = tcx.generics_of(item_def_id);
assert!(generics.parent.is_none());
Rc::new(vec![ty::Variance::Invariant; generics.count()])
}
let id = tcx.hir.as_local_node_id(item_def_id).expect("expected local def-id");
let unsupported = || {
// Variance not relevant.
span_bug!(tcx.hir.span(id), "asked to compute variance for wrong kind of item")
};
match tcx.hir.get(id) {
hir::map::NodeItem(item) => match item.node {
hir::ItemEnum(..) |
hir::ItemStruct(..) |
hir::ItemUnion(..) |
hir::ItemFn(..) => {}
hir::ItemEnum(..) |
hir::ItemStruct(..) |
hir::ItemUnion(..) => {
// Everything else must be inferred.
_ => unsupported()
},
// Lacking red/green, we read the variances for all items here
// but ignore the dependencies, then re-synthesize the ones we need.
let crate_map = tcx.dep_graph.with_ignore(|| tcx.crate_variances(LOCAL_CRATE));
let dep_node = item_def_id.to_dep_node(tcx, DepKind::ItemVarianceConstraints);
hir::map::NodeTraitItem(item) => match item.node {
hir::TraitItemKind::Method(..) => {}
_ => unsupported()
},
hir::map::NodeImplItem(item) => match item.node {
hir::ImplItemKind::Method(..) => {}
_ => unsupported()
},
hir::map::NodeForeignItem(item) => match item.node {
hir::ForeignItemFn(..) => {}
_ => unsupported()
},
hir::map::NodeVariant(_) | hir::map::NodeStructCtor(_) => {}
_ => unsupported()
}
// Everything else must be inferred.
// Lacking red/green, we read the variances for all items here
// but ignore the dependencies, then re-synthesize the ones we need.
let crate_map = tcx.dep_graph.with_ignore(|| tcx.crate_variances(LOCAL_CRATE));
let dep_node = item_def_id.to_dep_node(tcx, DepKind::ItemVarianceConstraints);
tcx.dep_graph.read(dep_node);
for &dep_def_id in crate_map.dependencies.less_than(&item_def_id) {
if dep_def_id.is_local() {
let dep_node = dep_def_id.to_dep_node(tcx, DepKind::ItemVarianceConstraints);
tcx.dep_graph.read(dep_node);
} else {
let dep_node = dep_def_id.to_dep_node(tcx, DepKind::ItemVariances);
tcx.dep_graph.read(dep_node);
for &dep_def_id in crate_map.dependencies.less_than(&item_def_id) {
if dep_def_id.is_local() {
let dep_node = dep_def_id.to_dep_node(tcx, DepKind::ItemVarianceConstraints);
tcx.dep_graph.read(dep_node);
} else {
let dep_node = dep_def_id.to_dep_node(tcx, DepKind::ItemVariances);
tcx.dep_graph.read(dep_node);
}
}
crate_map.variances.get(&item_def_id)
.unwrap_or(&crate_map.empty_variance)
.clone()
}
_ => {
// Variance not relevant.
span_bug!(item.span, "asked to compute variance for wrong kind of item")
}
}
crate_map.variances.get(&item_def_id)
.unwrap_or(&crate_map.empty_variance)
.clone()
}

View File

@ -36,15 +36,18 @@ struct SolveContext<'a, 'tcx: 'a> {
pub fn solve_constraints(constraints_cx: ConstraintContext) -> ty::CrateVariancesMap {
let ConstraintContext { terms_cx, dependencies, constraints, .. } = constraints_cx;
let solutions = terms_cx.inferred_infos
.iter()
.map(|ii| ii.initial_variance)
.collect();
let mut solutions = vec![ty::Bivariant; terms_cx.inferred_terms.len()];
for &(id, ref variances) in &terms_cx.lang_items {
let InferredIndex(start) = terms_cx.inferred_starts[&id];
for (i, &variance) in variances.iter().enumerate() {
solutions[start + i] = variance;
}
}
let mut solutions_cx = SolveContext {
terms_cx: terms_cx,
constraints: constraints,
solutions: solutions,
terms_cx,
constraints,
solutions,
};
solutions_cx.solve();
let variances = solutions_cx.create_map();
@ -71,12 +74,9 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> {
let old_value = self.solutions[inferred];
let new_value = glb(variance, old_value);
if old_value != new_value {
debug!("Updating inferred {} (node {}) \
debug!("Updating inferred {} \
from {:?} to {:?} due to {:?}",
inferred,
self.terms_cx
.inferred_infos[inferred]
.param_id,
old_value,
new_value,
term);
@ -89,49 +89,28 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> {
}
fn create_map(&self) -> FxHashMap<DefId, Rc<Vec<ty::Variance>>> {
// Collect all the variances for a particular item and stick
// them into the variance map. We rely on the fact that we
// generate all the inferreds for a particular item
// consecutively (that is, we collect solutions for an item
// until we see a new item id, and we assume (1) the solutions
// are in the same order as the type parameters were declared
// and (2) all solutions or a given item appear before a new
// item id).
let tcx = self.terms_cx.tcx;
let mut map = FxHashMap();
let solutions = &self.solutions;
let inferred_infos = &self.terms_cx.inferred_infos;
let mut index = 0;
let num_inferred = self.terms_cx.num_inferred();
while index < num_inferred {
let item_id = inferred_infos[index].item_id;
self.terms_cx.inferred_starts.iter().map(|(&id, &InferredIndex(start))| {
let def_id = tcx.hir.local_def_id(id);
let generics = tcx.generics_of(def_id);
let mut item_variances = vec![];
let mut variances = solutions[start..start+generics.count()].to_vec();
while index < num_inferred && inferred_infos[index].item_id == item_id {
let info = &inferred_infos[index];
let variance = solutions[index];
debug!("Index {} Info {} Variance {:?}",
index,
info.index,
variance);
debug!("id={} variances={:?}", id, variances);
assert_eq!(item_variances.len(), info.index);
item_variances.push(variance);
index += 1;
// Functions can have unused type parameters: make those invariant.
if let ty::TyFnDef(..) = tcx.type_of(def_id).sty {
for variance in &mut variances {
if *variance == ty::Bivariant {
*variance = ty::Invariant;
}
}
}
debug!("item_id={} item_variances={:?}", item_id, item_variances);
let item_def_id = tcx.hir.local_def_id(item_id);
map.insert(item_def_id, Rc::new(item_variances));
}
map
(def_id, Rc::new(variances))
}).collect()
}
fn evaluate(&self, term: VarianceTermPtr<'a>) -> ty::Variance {

View File

@ -22,7 +22,6 @@
use arena::TypedArena;
use rustc::ty::{self, TyCtxt};
use std::fmt;
use std::rc::Rc;
use syntax::ast;
use rustc::hir;
use rustc::hir::itemlikevisit::ItemLikeVisitor;
@ -63,31 +62,17 @@ pub struct TermsContext<'a, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub arena: &'a TypedArena<VarianceTerm<'a>>,
pub empty_variances: Rc<Vec<ty::Variance>>,
// For marker types, UnsafeCell, and other lang items where
// variance is hardcoded, records the item-id and the hardcoded
// variance.
pub lang_items: Vec<(ast::NodeId, Vec<ty::Variance>)>,
// Maps from the node id of a type/generic parameter to the
// corresponding inferred index.
pub inferred_map: NodeMap<InferredIndex>,
// Maps from the node id of an item to the first inferred index
// used for its type & region parameters.
pub inferred_starts: NodeMap<InferredIndex>,
// Maps from an InferredIndex to the info for that variable.
pub inferred_infos: Vec<InferredInfo<'a>>,
}
pub struct InferredInfo<'a> {
pub item_id: ast::NodeId,
pub index: usize,
pub param_id: ast::NodeId,
pub term: VarianceTermPtr<'a>,
// Initial value to use for this parameter when inferring
// variance. For most parameters, this is Bivariant. But for lang
// items and input type parameters on traits, it is different.
pub initial_variance: ty::Variance,
// Maps from an InferredIndex to the term for that variable.
pub inferred_terms: Vec<VarianceTermPtr<'a>>,
}
pub fn determine_parameters_to_be_inferred<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@ -96,14 +81,10 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>
let mut terms_cx = TermsContext {
tcx: tcx,
arena: arena,
inferred_map: NodeMap(),
inferred_infos: Vec::new(),
inferred_starts: NodeMap(),
inferred_terms: vec![],
lang_items: lang_items(tcx),
// cache and share the variance struct used for items with
// no type/region parameters
empty_variances: Rc::new(vec![]),
};
// See README.md for a discussion on dep-graph management.
@ -135,67 +116,28 @@ fn lang_items(tcx: TyCtxt) -> Vec<(ast::NodeId, Vec<ty::Variance>)> {
}
impl<'a, 'tcx> TermsContext<'a, 'tcx> {
fn add_inferreds_for_item(&mut self,
item_id: ast::NodeId,
generics: &hir::Generics) {
//! Add "inferreds" for the generic parameters declared on this
//! item. This has a lot of annoying parameters because we are
//! trying to drive this from the AST, rather than the
//! ty::Generics, so that we can get span info -- but this
//! means we must accommodate syntactic distinctions.
//!
fn add_inferreds_for_item(&mut self, id: ast::NodeId) {
let tcx = self.tcx;
let def_id = tcx.hir.local_def_id(id);
let count = tcx.generics_of(def_id).count();
if count == 0 {
return;
}
// Record the start of this item's inferreds.
let start = self.inferred_terms.len();
let newly_added = self.inferred_starts.insert(id, InferredIndex(start)).is_none();
assert!(newly_added);
// NB: In the code below for writing the results back into the
// `CrateVariancesMap`, we rely on the fact that all inferreds
// for a particular item are assigned continuous indices.
for (p, i) in generics.lifetimes.iter().zip(0..) {
let id = p.lifetime.id;
self.add_inferred(item_id, i, id);
}
for (p, i) in generics.ty_params.iter().zip(generics.lifetimes.len()..) {
self.add_inferred(item_id, i, p.id);
}
}
fn add_inferred(&mut self, item_id: ast::NodeId, index: usize, param_id: ast::NodeId) {
let inf_index = InferredIndex(self.inferred_infos.len());
let term = self.arena.alloc(InferredTerm(inf_index));
let initial_variance = self.pick_initial_variance(item_id, index);
self.inferred_infos.push(InferredInfo {
item_id: item_id,
index: index,
param_id: param_id,
term: term,
initial_variance: initial_variance,
});
let newly_added = self.inferred_map.insert(param_id, inf_index).is_none();
assert!(newly_added);
debug!("add_inferred(item_path={}, \
item_id={}, \
index={}, \
param_id={}, \
inf_index={:?}, \
initial_variance={:?})",
self.tcx.item_path_str(self.tcx.hir.local_def_id(item_id)),
item_id,
index,
param_id,
inf_index,
initial_variance);
}
fn pick_initial_variance(&self, item_id: ast::NodeId, index: usize) -> ty::Variance {
match self.lang_items.iter().find(|&&(n, _)| n == item_id) {
Some(&(_, ref variances)) => variances[index],
None => ty::Bivariant,
}
}
pub fn num_inferred(&self) -> usize {
self.inferred_infos.len()
let arena = self.arena;
self.inferred_terms.extend((start..start+count).map(|i| {
&*arena.alloc(InferredTerm(InferredIndex(i)))
}));
}
}
@ -205,30 +147,50 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for TermsContext<'a, 'tcx> {
self.tcx.hir.node_to_string(item.id));
match item.node {
hir::ItemEnum(_, ref generics) |
hir::ItemStruct(_, ref generics) |
hir::ItemUnion(_, ref generics) => {
self.add_inferreds_for_item(item.id, generics);
hir::ItemStruct(ref struct_def, _) |
hir::ItemUnion(ref struct_def, _) => {
self.add_inferreds_for_item(item.id);
if let hir::VariantData::Tuple(..) = *struct_def {
self.add_inferreds_for_item(struct_def.id());
}
}
hir::ItemTrait(..) |
hir::ItemExternCrate(_) |
hir::ItemUse(..) |
hir::ItemDefaultImpl(..) |
hir::ItemImpl(..) |
hir::ItemStatic(..) |
hir::ItemConst(..) |
hir::ItemFn(..) |
hir::ItemMod(..) |
hir::ItemForeignMod(..) |
hir::ItemGlobalAsm(..) |
hir::ItemTy(..) => {}
hir::ItemEnum(ref enum_def, _) => {
self.add_inferreds_for_item(item.id);
for variant in &enum_def.variants {
if let hir::VariantData::Tuple(..) = variant.node.data {
self.add_inferreds_for_item(variant.node.data.id());
}
}
}
hir::ItemFn(..) => {
self.add_inferreds_for_item(item.id);
}
hir::ItemForeignMod(ref foreign_mod) => {
for foreign_item in &foreign_mod.items {
if let hir::ForeignItemFn(..) = foreign_item.node {
self.add_inferreds_for_item(foreign_item.id);
}
}
}
_ => {}
}
}
fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) {
fn visit_trait_item(&mut self, trait_item: &hir::TraitItem) {
if let hir::TraitItemKind::Method(..) = trait_item.node {
self.add_inferreds_for_item(trait_item.id);
}
}
fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
fn visit_impl_item(&mut self, impl_item: &hir::ImplItem) {
if let hir::ImplItemKind::Method(..) = impl_item.node {
self.add_inferreds_for_item(impl_item.id);
}
}
}

View File

@ -1,25 +0,0 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Check that `T:'a` is contravariant in T.
#![feature(rustc_attrs)]
#[rustc_variance]
trait Foo: 'static { //~ ERROR [o]
}
#[rustc_variance]
trait Bar<T> { //~ ERROR [o, o]
fn do_it(&self)
where T: 'static;
}
fn main() { }

View File

@ -14,13 +14,11 @@
// Check that bounds on type parameters (other than `Self`) do not
// influence variance.
#[rustc_variance]
trait Getter<T> { //~ ERROR [o, o]
trait Getter<T> {
fn get(&self) -> T;
}
#[rustc_variance]
trait Setter<T> { //~ ERROR [o, o]
trait Setter<T> {
fn get(&self, T);
}
@ -34,20 +32,6 @@ enum TestEnum<U,T:Setter<U>> { //~ ERROR [*, +]
Foo(T)
}
#[rustc_variance]
trait TestTrait<U,T:Setter<U>> { //~ ERROR [o, o, o]
fn getter(&self, u: U) -> T;
}
#[rustc_variance]
trait TestTrait2<U> : Getter<U> { //~ ERROR [o, o]
}
#[rustc_variance]
trait TestTrait3<U> { //~ ERROR [o, o]
fn getter<T:Getter<U>>(&self);
}
#[rustc_variance]
struct TestContraStruct<U,T:Setter<U>> { //~ ERROR [*, +]
t: T

View File

@ -36,37 +36,14 @@ struct TestIndirect2<A:'static, B:'static> { //~ ERROR [o, o]
m: TestMut<B, A>
}
#[rustc_variance]
trait Getter<A> { //~ ERROR [o, o]
trait Getter<A> {
fn get(&self) -> A;
}
#[rustc_variance]
trait Setter<A> { //~ ERROR [o, o]
trait Setter<A> {
fn set(&mut self, a: A);
}
#[rustc_variance]
trait GetterSetter<A> { //~ ERROR [o, o]
fn get(&self) -> A;
fn set(&mut self, a: A);
}
#[rustc_variance]
trait GetterInTypeBound<A> { //~ ERROR [o, o]
// Here, the use of `A` in the method bound *does* affect
// variance. Think of it as if the method requested a dictionary
// for `T:Getter<A>`. Since this dictionary is an input, it is
// contravariant, and the Getter is covariant w/r/t A, yielding an
// overall contravariant result.
fn do_it<T:Getter<A>>(&self);
}
#[rustc_variance]
trait SetterInTypeBound<A> { //~ ERROR [o, o]
fn do_it<T:Setter<A>>(&self);
}
#[rustc_variance]
struct TestObject<A, R> { //~ ERROR [o, o]
n: Box<Setter<A>+Send>,