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 @@ fn relate<'a, 'gcx, R>(relation: &mut R,
if a.def_id != b.def_id { if a.def_id != b.def_id {
Err(TypeError::Traits(expected_found(relation, &a.def_id, &b.def_id))) Err(TypeError::Traits(expected_found(relation, &a.def_id, &b.def_id)))
} else { } 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 }) Ok(ty::TraitRef { def_id: a.def_id, substs: substs })
} }
} }
@ -308,7 +308,7 @@ fn relate<'a, 'gcx, R>(relation: &mut R,
if a.def_id != b.def_id { if a.def_id != b.def_id {
Err(TypeError::Traits(expected_found(relation, &a.def_id, &b.def_id))) Err(TypeError::Traits(expected_found(relation, &a.def_id, &b.def_id)))
} else { } 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 }) 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)) (&ty::TyFnDef(a_def_id, a_substs), &ty::TyFnDef(b_def_id, b_substs))
if a_def_id == b_def_id => 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)) Ok(tcx.mk_fn_def(a_def_id, substs))
} }

View File

@ -524,7 +524,11 @@ fn encode_enum_variant_info(&mut self,
ty: Some(self.encode_item_type(def_id)), ty: Some(self.encode_item_type(def_id)),
inherent_impls: LazySeq::empty(), 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)), generics: Some(self.encode_generics(def_id)),
predicates: Some(self.encode_predicates(def_id)), predicates: Some(self.encode_predicates(def_id)),
@ -652,7 +656,11 @@ fn encode_struct_ctor(&mut self, (adt_def_id, def_id): (DefId, DefId)) -> Entry<
ty: Some(self.encode_item_type(def_id)), ty: Some(self.encode_item_type(def_id)),
inherent_impls: LazySeq::empty(), 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)), generics: Some(self.encode_generics(def_id)),
predicates: Some(self.encode_predicates(def_id)), predicates: Some(self.encode_predicates(def_id)),
@ -744,7 +752,11 @@ fn encode_info_for_trait_item(&mut self, def_id: DefId) -> Entry<'tcx> {
} }
}, },
inherent_impls: LazySeq::empty(), 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)), generics: Some(self.encode_generics(def_id)),
predicates: Some(self.encode_predicates(def_id)), predicates: Some(self.encode_predicates(def_id)),
@ -821,7 +833,11 @@ fn encode_info_for_impl_item(&mut self, def_id: DefId) -> Entry<'tcx> {
ty: Some(self.encode_item_type(def_id)), ty: Some(self.encode_item_type(def_id)),
inherent_impls: LazySeq::empty(), 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)), generics: Some(self.encode_generics(def_id)),
predicates: Some(self.encode_predicates(def_id)), predicates: Some(self.encode_predicates(def_id)),
@ -1055,7 +1071,7 @@ fn encode_info_for_item(&mut self, (def_id, item): (DefId, &'tcx hir::Item)) ->
hir::ItemEnum(..) | hir::ItemEnum(..) |
hir::ItemStruct(..) | hir::ItemStruct(..) |
hir::ItemUnion(..) | hir::ItemUnion(..) |
hir::ItemTrait(..) => self.encode_variances_of(def_id), hir::ItemFn(..) => self.encode_variances_of(def_id),
_ => LazySeq::empty(), _ => LazySeq::empty(),
}, },
generics: match item.node { generics: match item.node {
@ -1400,7 +1416,10 @@ fn encode_info_for_foreign_item(&mut self,
ty: Some(self.encode_item_type(def_id)), ty: Some(self.encode_item_type(def_id)),
inherent_impls: LazySeq::empty(), 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)), generics: Some(self.encode_generics(def_id)),
predicates: Some(self.encode_predicates(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. //! We walk the set of items and, for each member, generate new constraints.
use hir::def_id::DefId; use hir::def_id::DefId;
use middle::resolve_lifetime as rl;
use rustc::dep_graph::{AssertDepGraphSafe, DepKind}; use rustc::dep_graph::{AssertDepGraphSafe, DepKind};
use rustc::ty::subst::Substs; use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::{self, Ty, TyCtxt};
use rustc::hir::map as hir_map;
use syntax::ast; use syntax::ast;
use rustc::hir; use rustc::hir;
use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::itemlikevisit::ItemLikeVisitor;
@ -61,10 +59,10 @@ pub struct Constraint<'a> {
/// } /// }
/// ///
/// then while we are visiting `Bar<T>`, the `CurrentItem` would have /// then while we are visiting `Bar<T>`, the `CurrentItem` would have
/// the def-id and generics of `Foo`. /// the def-id and the start of `Foo`'s inferreds.
pub struct CurrentItem<'a> { pub struct CurrentItem {
def_id: DefId, def_id: DefId,
generics: &'a ty::Generics, inferred_start: InferredIndex,
} }
pub fn add_constraints_from_crate<'a, 'tcx>(terms_cx: TermsContext<'a, 'tcx>) 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> { impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> {
fn visit_item(&mut self, item: &hir::Item) { 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 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 // Encapsulate constructing the constraints into a task we can
// reference later. This can go away once the red-green // reference later. This can go away once the red-green
@ -100,20 +149,11 @@ fn visit_item(&mut self, item: &hir::Item) {
// //
// See README.md for a detailed discussion // See README.md for a detailed discussion
// on dep-graph management. // on dep-graph management.
match item.node { let dep_node = def_id.to_dep_node(tcx, DepKind::ItemVarianceConstraints);
hir::ItemEnum(..) | tcx.dep_graph.with_task(dep_node,
hir::ItemStruct(..) | AssertDepGraphSafe(self),
hir::ItemUnion(..) => { def_id,
let dep_node = def_id.to_dep_node(tcx, DepKind::ItemVarianceConstraints); visit_item_task);
tcx.dep_graph.with_task(dep_node,
AssertDepGraphSafe(self),
def_id,
visit_item_task);
}
_ => {
// Nothing to do here, skip the task.
}
}
fn visit_item_task<'a, 'tcx>(ccx: AssertDepGraphSafe<&mut ConstraintContext<'a, 'tcx>>, fn visit_item_task<'a, 'tcx>(ccx: AssertDepGraphSafe<&mut ConstraintContext<'a, 'tcx>>,
def_id: DefId) def_id: DefId)
@ -122,197 +162,57 @@ fn visit_item_task<'a, 'tcx>(ccx: AssertDepGraphSafe<&mut ConstraintContext<'a,
} }
} }
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> { fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> {
self.terms_cx.tcx self.terms_cx.tcx
} }
fn build_constraints_for_item(&mut self, def_id: DefId) { fn build_constraints_for_item(&mut self, def_id: DefId) {
let tcx = self.tcx(); let tcx = self.tcx();
let id = self.tcx().hir.as_local_node_id(def_id).unwrap(); debug!("build_constraints_for_item({})", tcx.item_path_str(def_id));
let item = tcx.hir.expect_item(id);
debug!("visit_item item={}", tcx.hir.node_to_string(item.id));
match item.node { // Skip items with no generics - there's nothing to infer in them.
hir::ItemEnum(..) | if tcx.generics_of(def_id).count() == 0 {
hir::ItemStruct(..) | return;
hir::ItemUnion(..) => { }
let generics = tcx.generics_of(def_id);
let current_item = &CurrentItem { def_id, generics };
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 // Not entirely obvious: constraints on structs/enums do not
// affect the variance of their type parameters. See discussion // affect the variance of their type parameters. See discussion
// in comment at top of module. // in comment at top of module.
// //
// self.add_constraints_from_generics(generics); // 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, self.add_constraints_from_ty(current_item,
tcx.type_of(field.did), tcx.type_of(field.did),
self.covariant); self.covariant);
} }
} }
hir::ItemTrait(..) | ty::TyFnDef(..) => {
hir::ItemExternCrate(_) | self.add_constraints_from_sig(current_item,
hir::ItemUse(..) | tcx.fn_sig(def_id),
hir::ItemStatic(..) | self.covariant);
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!(),
} }
return is_inferred; _ => {
}; span_bug!(tcx.def_span(def_id),
"`build_constraints_for_item` unsupported for this item");
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
} }
} 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, fn add_constraint(&mut self,
InferredIndex(index): InferredIndex, current: &CurrentItem,
index: u32,
variance: VarianceTermPtr<'a>) { variance: VarianceTermPtr<'a>) {
debug!("add_constraint(index={}, variance={:?})", index, variance); debug!("add_constraint(index={}, variance={:?})", index, variance);
self.constraints.push(Constraint { self.constraints.push(Constraint {
inferred: InferredIndex(index), inferred: InferredIndex(current.inferred_start.0 + index as usize),
variance: variance, variance: variance,
}); });
} }
@ -354,15 +254,26 @@ fn add_constraints_from_trait_ref(&mut self,
debug!("add_constraints_from_trait_ref: trait_ref={:?} variance={:?}", debug!("add_constraints_from_trait_ref: trait_ref={:?} variance={:?}",
trait_ref, trait_ref,
variance); 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 are always invariant so we can take advantage of that.
trait_ref.def_id, let variance_i = self.invariant(variance);
&trait_generics.types, for ty in substs.types() {
&trait_generics.regions, self.add_constraints_from_ty(current, ty, variance_i);
trait_ref.substs, }
variance);
for region in substs.regions() {
self.add_constraints_from_region(current, region, variance_i);
}
} }
/// Adds constraints appropriate for an instance of `ty` appearing /// Adds constraints appropriate for an instance of `ty` appearing
@ -383,8 +294,7 @@ fn add_constraints_from_ty(&mut self,
} }
ty::TyFnDef(..) | ty::TyFnDef(..) |
ty::TyClosure(..) | ty::TyClosure(..) => {
ty::TyAnon(..) => {
bug!("Unexpected closure type in variance computation"); bug!("Unexpected closure type in variance computation");
} }
@ -410,26 +320,15 @@ fn add_constraints_from_ty(&mut self,
} }
ty::TyAdt(def, substs) => { ty::TyAdt(def, substs) => {
let adt_generics = self.read_generics(current, def.did); self.add_constraints_from_substs(current, def.did, substs, variance);
self.add_constraints_from_substs(current,
def.did,
&adt_generics.types,
&adt_generics.regions,
substs,
variance);
} }
ty::TyProjection(ref data) => { ty::TyProjection(ref data) => {
let trait_ref = &data.trait_ref; self.add_constraints_from_trait_ref(current, data.trait_ref, variance);
let trait_generics = self.tcx().generics_of(trait_ref.def_id); }
self.add_constraints_from_substs(current, ty::TyAnon(_, substs) => {
trait_ref.def_id, self.add_constraints_from_invariant_substs(current, substs, variance);
&trait_generics.types,
&trait_generics.regions,
trait_ref.substs,
variance);
} }
ty::TyDynamic(ref data, r) => { ty::TyDynamic(ref data, r) => {
@ -448,23 +347,7 @@ fn add_constraints_from_ty(&mut self,
} }
ty::TyParam(ref data) => { ty::TyParam(ref data) => {
assert_eq!(current.generics.parent, None); self.add_constraint(current, data.idx, variance);
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.
}
}
} }
ty::TyFnPtr(sig) => { ty::TyFnPtr(sig) => {
@ -489,8 +372,6 @@ fn add_constraints_from_ty(&mut self,
fn add_constraints_from_substs(&mut self, fn add_constraints_from_substs(&mut self,
current: &CurrentItem, current: &CurrentItem,
def_id: DefId, def_id: DefId,
type_param_defs: &[ty::TypeParameterDef],
region_param_defs: &[ty::RegionParameterDef],
substs: &Substs<'tcx>, substs: &Substs<'tcx>,
variance: VarianceTermPtr<'a>) { variance: VarianceTermPtr<'a>) {
debug!("add_constraints_from_substs(def_id={:?}, substs={:?}, variance={:?})", debug!("add_constraints_from_substs(def_id={:?}, substs={:?}, variance={:?})",
@ -498,21 +379,45 @@ fn add_constraints_from_substs(&mut self,
substs, substs,
variance); variance);
for p in type_param_defs { // We don't record `inferred_starts` entries for empty generics.
let variance_decl = self.declared_variance(p.def_id, def_id, p.index as usize); 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 variance_i = self.xform(variance, variance_decl);
let substs_ty = substs.type_for_def(p);
debug!("add_constraints_from_substs: variance_decl={:?} variance_i={:?}", debug!("add_constraints_from_substs: variance_decl={:?} variance_i={:?}",
variance_decl, variance_decl,
variance_i); variance_i);
self.add_constraints_from_ty(current, substs_ty, 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() {
for p in region_param_defs { self.add_constraints_from_region(current, r, variance_i);
let variance_decl = self.declared_variance(p.def_id, def_id, p.index as usize); } else {
let variance_i = self.xform(variance, variance_decl); bug!();
let substs_r = substs.region_for_def(p); }
self.add_constraints_from_region(current, substs_r, variance_i);
} }
} }
@ -537,21 +442,14 @@ fn add_constraints_from_region(&mut self,
variance: VarianceTermPtr<'a>) { variance: VarianceTermPtr<'a>) {
match *region { match *region {
ty::ReEarlyBound(ref data) => { ty::ReEarlyBound(ref data) => {
assert_eq!(current.generics.parent, None); self.add_constraint(current, data.index, variance);
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);
}
} }
ty::ReStatic => {} ty::ReStatic => {}
ty::ReLateBound(..) => { ty::ReLateBound(..) => {
// We do not infer variance for region parameters on // Late-bound regions do not get substituted the same
// methods or in fn types. // way early-bound regions do, so we skip them here.
} }
ty::ReFree(..) | 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) fn variances_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_def_id: DefId)
-> Rc<Vec<ty::Variance>> { -> Rc<Vec<ty::Variance>> {
let item_id = tcx.hir.as_local_node_id(item_def_id).expect("expected local def-id"); let id = tcx.hir.as_local_node_id(item_def_id).expect("expected local def-id");
let item = tcx.hir.expect_item(item_id); let unsupported = || {
match item.node { // Variance not relevant.
hir::ItemTrait(..) => { span_bug!(tcx.hir.span(id), "asked to compute variance for wrong kind of item")
// Traits are always invariant. };
let generics = tcx.generics_of(item_def_id); match tcx.hir.get(id) {
assert!(generics.parent.is_none()); hir::map::NodeItem(item) => match item.node {
Rc::new(vec![ty::Variance::Invariant; generics.count()]) hir::ItemEnum(..) |
} hir::ItemStruct(..) |
hir::ItemUnion(..) |
hir::ItemFn(..) => {}
hir::ItemEnum(..) | _ => unsupported()
hir::ItemStruct(..) | },
hir::ItemUnion(..) => {
// Everything else must be inferred.
// Lacking red/green, we read the variances for all items here hir::map::NodeTraitItem(item) => match item.node {
// but ignore the dependencies, then re-synthesize the ones we need. hir::TraitItemKind::Method(..) => {}
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); _ => 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); 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 { pub fn solve_constraints(constraints_cx: ConstraintContext) -> ty::CrateVariancesMap {
let ConstraintContext { terms_cx, dependencies, constraints, .. } = constraints_cx; let ConstraintContext { terms_cx, dependencies, constraints, .. } = constraints_cx;
let solutions = terms_cx.inferred_infos let mut solutions = vec![ty::Bivariant; terms_cx.inferred_terms.len()];
.iter() for &(id, ref variances) in &terms_cx.lang_items {
.map(|ii| ii.initial_variance) let InferredIndex(start) = terms_cx.inferred_starts[&id];
.collect(); for (i, &variance) in variances.iter().enumerate() {
solutions[start + i] = variance;
}
}
let mut solutions_cx = SolveContext { let mut solutions_cx = SolveContext {
terms_cx: terms_cx, terms_cx,
constraints: constraints, constraints,
solutions: solutions, solutions,
}; };
solutions_cx.solve(); solutions_cx.solve();
let variances = solutions_cx.create_map(); let variances = solutions_cx.create_map();
@ -71,12 +74,9 @@ fn solve(&mut self) {
let old_value = self.solutions[inferred]; let old_value = self.solutions[inferred];
let new_value = glb(variance, old_value); let new_value = glb(variance, old_value);
if old_value != new_value { if old_value != new_value {
debug!("Updating inferred {} (node {}) \ debug!("Updating inferred {} \
from {:?} to {:?} due to {:?}", from {:?} to {:?} due to {:?}",
inferred, inferred,
self.terms_cx
.inferred_infos[inferred]
.param_id,
old_value, old_value,
new_value, new_value,
term); term);
@ -89,49 +89,28 @@ fn solve(&mut self) {
} }
fn create_map(&self) -> FxHashMap<DefId, Rc<Vec<ty::Variance>>> { 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 tcx = self.terms_cx.tcx;
let mut map = FxHashMap();
let solutions = &self.solutions; let solutions = &self.solutions;
let inferred_infos = &self.terms_cx.inferred_infos; self.terms_cx.inferred_starts.iter().map(|(&id, &InferredIndex(start))| {
let mut index = 0; let def_id = tcx.hir.local_def_id(id);
let num_inferred = self.terms_cx.num_inferred(); let generics = tcx.generics_of(def_id);
while index < num_inferred {
let item_id = inferred_infos[index].item_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 { debug!("id={} variances={:?}", id, variances);
let info = &inferred_infos[index];
let variance = solutions[index];
debug!("Index {} Info {} Variance {:?}",
index,
info.index,
variance);
assert_eq!(item_variances.len(), info.index); // Functions can have unused type parameters: make those invariant.
item_variances.push(variance); if let ty::TyFnDef(..) = tcx.type_of(def_id).sty {
index += 1; for variance in &mut variances {
if *variance == ty::Bivariant {
*variance = ty::Invariant;
}
}
} }
debug!("item_id={} item_variances={:?}", item_id, item_variances); (def_id, Rc::new(variances))
}).collect()
let item_def_id = tcx.hir.local_def_id(item_id);
map.insert(item_def_id, Rc::new(item_variances));
}
map
} }
fn evaluate(&self, term: VarianceTermPtr<'a>) -> ty::Variance { fn evaluate(&self, term: VarianceTermPtr<'a>) -> ty::Variance {

View File

@ -22,7 +22,6 @@
use arena::TypedArena; use arena::TypedArena;
use rustc::ty::{self, TyCtxt}; use rustc::ty::{self, TyCtxt};
use std::fmt; use std::fmt;
use std::rc::Rc;
use syntax::ast; use syntax::ast;
use rustc::hir; use rustc::hir;
use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::itemlikevisit::ItemLikeVisitor;
@ -63,31 +62,17 @@ pub struct TermsContext<'a, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'tcx, 'tcx>, pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub arena: &'a TypedArena<VarianceTerm<'a>>, pub arena: &'a TypedArena<VarianceTerm<'a>>,
pub empty_variances: Rc<Vec<ty::Variance>>,
// For marker types, UnsafeCell, and other lang items where // For marker types, UnsafeCell, and other lang items where
// variance is hardcoded, records the item-id and the hardcoded // variance is hardcoded, records the item-id and the hardcoded
// variance. // variance.
pub lang_items: Vec<(ast::NodeId, Vec<ty::Variance>)>, pub lang_items: Vec<(ast::NodeId, Vec<ty::Variance>)>,
// Maps from the node id of a type/generic parameter to the // Maps from the node id of an item to the first inferred index
// corresponding inferred index. // used for its type & region parameters.
pub inferred_map: NodeMap<InferredIndex>, pub inferred_starts: NodeMap<InferredIndex>,
// Maps from an InferredIndex to the info for that variable. // Maps from an InferredIndex to the term for that variable.
pub inferred_infos: Vec<InferredInfo<'a>>, pub inferred_terms: Vec<VarianceTermPtr<'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,
} }
pub fn determine_parameters_to_be_inferred<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, 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 { let mut terms_cx = TermsContext {
tcx: tcx, tcx: tcx,
arena: arena, arena: arena,
inferred_map: NodeMap(), inferred_starts: NodeMap(),
inferred_infos: Vec::new(), inferred_terms: vec![],
lang_items: lang_items(tcx), 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. // 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> { impl<'a, 'tcx> TermsContext<'a, 'tcx> {
fn add_inferreds_for_item(&mut self, fn add_inferreds_for_item(&mut self, id: ast::NodeId) {
item_id: ast::NodeId, let tcx = self.tcx;
generics: &hir::Generics) { let def_id = tcx.hir.local_def_id(id);
//! Add "inferreds" for the generic parameters declared on this let count = tcx.generics_of(def_id).count();
//! item. This has a lot of annoying parameters because we are
//! trying to drive this from the AST, rather than the if count == 0 {
//! ty::Generics, so that we can get span info -- but this return;
//! means we must accommodate syntactic distinctions. }
//!
// 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 // NB: In the code below for writing the results back into the
// `CrateVariancesMap`, we rely on the fact that all inferreds // `CrateVariancesMap`, we rely on the fact that all inferreds
// for a particular item are assigned continuous indices. // for a particular item are assigned continuous indices.
for (p, i) in generics.lifetimes.iter().zip(0..) { let arena = self.arena;
let id = p.lifetime.id; self.inferred_terms.extend((start..start+count).map(|i| {
self.add_inferred(item_id, i, id); &*arena.alloc(InferredTerm(InferredIndex(i)))
} }));
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()
} }
} }
@ -205,30 +147,50 @@ fn visit_item(&mut self, item: &hir::Item) {
self.tcx.hir.node_to_string(item.id)); self.tcx.hir.node_to_string(item.id));
match item.node { match item.node {
hir::ItemEnum(_, ref generics) | hir::ItemStruct(ref struct_def, _) |
hir::ItemStruct(_, ref generics) | hir::ItemUnion(ref struct_def, _) => {
hir::ItemUnion(_, ref generics) => { self.add_inferreds_for_item(item.id);
self.add_inferreds_for_item(item.id, generics);
if let hir::VariantData::Tuple(..) = *struct_def {
self.add_inferreds_for_item(struct_def.id());
}
} }
hir::ItemTrait(..) | hir::ItemEnum(ref enum_def, _) => {
hir::ItemExternCrate(_) | self.add_inferreds_for_item(item.id);
hir::ItemUse(..) |
hir::ItemDefaultImpl(..) | for variant in &enum_def.variants {
hir::ItemImpl(..) | if let hir::VariantData::Tuple(..) = variant.node.data {
hir::ItemStatic(..) | self.add_inferreds_for_item(variant.node.data.id());
hir::ItemConst(..) | }
hir::ItemFn(..) | }
hir::ItemMod(..) | }
hir::ItemForeignMod(..) |
hir::ItemGlobalAsm(..) | hir::ItemFn(..) => {
hir::ItemTy(..) => {} 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 // Check that bounds on type parameters (other than `Self`) do not
// influence variance. // influence variance.
#[rustc_variance] trait Getter<T> {
trait Getter<T> { //~ ERROR [o, o]
fn get(&self) -> T; fn get(&self) -> T;
} }
#[rustc_variance] trait Setter<T> {
trait Setter<T> { //~ ERROR [o, o]
fn get(&self, T); fn get(&self, T);
} }
@ -34,20 +32,6 @@ enum TestEnum<U,T:Setter<U>> { //~ ERROR [*, +]
Foo(T) 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] #[rustc_variance]
struct TestContraStruct<U,T:Setter<U>> { //~ ERROR [*, +] struct TestContraStruct<U,T:Setter<U>> { //~ ERROR [*, +]
t: T t: T

View File

@ -36,37 +36,14 @@ struct TestIndirect2<A:'static, B:'static> { //~ ERROR [o, o]
m: TestMut<B, A> m: TestMut<B, A>
} }
#[rustc_variance] trait Getter<A> {
trait Getter<A> { //~ ERROR [o, o]
fn get(&self) -> A; fn get(&self) -> A;
} }
#[rustc_variance] trait Setter<A> {
trait Setter<A> { //~ ERROR [o, o]
fn set(&mut self, a: 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] #[rustc_variance]
struct TestObject<A, R> { //~ ERROR [o, o] struct TestObject<A, R> { //~ ERROR [o, o]
n: Box<Setter<A>+Send>, n: Box<Setter<A>+Send>,