Skip to content

There is no variance to infer for generics that occur in non-variance contexts #13261

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

Merged
merged 5 commits into from
Apr 17, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions src/librustc/middle/trans/monomorphize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
}

// Ugh -- but this ensures any new variants won't be forgotten
ast_map::NodeLifetime(..) |
ast_map::NodeExpr(..) |
ast_map::NodeStmt(..) |
ast_map::NodeArg(..) |
Expand Down
78 changes: 76 additions & 2 deletions src/librustc/middle/typeck/variance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ use arena::Arena;
use middle::ty;
use std::fmt;
use syntax::ast;
use syntax::ast_map;
use syntax::ast_util;
use syntax::owned_slice::OwnedSlice;
use syntax::visit;
Expand Down Expand Up @@ -517,6 +518,13 @@ impl<'a> Visitor<()> for ConstraintContext<'a> {
}
}

/// Is `param_id` a lifetime according to `map`?
fn is_lifetime(map: &ast_map::Map, param_id: ast::NodeId) -> bool {
match map.find(param_id) {
Some(ast_map::NodeLifetime(..)) => true, _ => false
}
}

impl<'a> ConstraintContext<'a> {
fn tcx(&self) -> &'a ty::ctxt {
self.terms_cx.tcx
Expand All @@ -533,6 +541,70 @@ impl<'a> ConstraintContext<'a> {
}
}

fn find_binding_for_lifetime(&self, param_id: ast::NodeId) -> ast::NodeId {
let tcx = self.terms_cx.tcx;
assert!(is_lifetime(&tcx.map, param_id));
match tcx.named_region_map.find(&param_id) {
Some(&ast::DefEarlyBoundRegion(_, lifetime_decl_id))
=> lifetime_decl_id,
Some(_) => fail!("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.map, param_id));
let parent_id = tcx.map.get_parent(decl_id);
let parent = tcx.map.find(parent_id).unwrap_or_else(
|| fail!("tcx.map missing entry for id: {}", parent_id));

let is_inferred;
macro_rules! cannot_happen { () => { {
fail!("invalid parent: {:s} for {:s}",
tcx.map.node_to_str(parent_id),
tcx.map.node_to_str(param_id));
} } }

match parent {
ast_map::NodeItem(p) => {
match p.node {
ast::ItemTy(..) |
ast::ItemEnum(..) |
ast::ItemStruct(..) |
ast::ItemTrait(..) => is_inferred = true,
ast::ItemFn(..) => is_inferred = false,
_ => cannot_happen!(),
}
}
ast_map::NodeTraitMethod(..) => is_inferred = false,
ast_map::NodeMethod(_) => is_inferred = false,
_ => cannot_happen!(),
}

return is_inferred;
};

assert_eq!(result, check_result(self));

return result;
}

fn declared_variance(&self,
param_def_id: ast::DefId,
item_def_id: ast::DefId,
Expand Down Expand Up @@ -788,8 +860,10 @@ impl<'a> ConstraintContext<'a> {
variance: VarianceTermPtr<'a>) {
match region {
ty::ReEarlyBound(param_id, _, _) => {
let index = self.inferred_index(param_id);
self.add_constraint(index, variance);
if self.is_to_be_inferred(param_id) {
let index = self.inferred_index(param_id);
self.add_constraint(index, variance);
}
}

ty::ReStatic => { }
Expand Down
25 changes: 25 additions & 0 deletions src/libsyntax/ast_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ pub enum Node {

/// NodeStructCtor represents a tuple struct.
NodeStructCtor(@StructDef),

NodeLifetime(@Lifetime),
}

// The odd layout is to bring down the total size.
Expand All @@ -127,6 +129,7 @@ enum MapEntry {
EntryLocal(NodeId, @Pat),
EntryBlock(NodeId, P<Block>),
EntryStructCtor(NodeId, @StructDef),
EntryLifetime(NodeId, @Lifetime),

// Roots for node trees.
RootCrate,
Expand All @@ -153,6 +156,7 @@ impl MapEntry {
EntryLocal(id, _) => id,
EntryBlock(id, _) => id,
EntryStructCtor(id, _) => id,
EntryLifetime(id, _) => id,
_ => return None
})
}
Expand All @@ -170,6 +174,7 @@ impl MapEntry {
EntryLocal(_, p) => NodeLocal(p),
EntryBlock(_, p) => NodeBlock(p),
EntryStructCtor(_, p) => NodeStructCtor(p),
EntryLifetime(_, p) => NodeLifetime(p),
_ => return None
})
}
Expand Down Expand Up @@ -213,6 +218,8 @@ impl Map {
self.find_entry(id).and_then(|x| x.to_node())
}

/// Retrieve the parent NodeId for `id`, or `id` itself if no
/// parent is registered in this map.
pub fn get_parent(&self, id: NodeId) -> NodeId {
self.find_entry(id).and_then(|x| x.parent()).unwrap_or(id)
}
Expand Down Expand Up @@ -500,6 +507,15 @@ impl<'a, F: FoldOps> Folder for Ctx<'a, F> {
SmallVector::one(stmt)
}

fn fold_type_method(&mut self, m: &TypeMethod) -> TypeMethod {
let parent = self.parent;
self.parent = DUMMY_NODE_ID;
let m = fold::noop_fold_type_method(m, self);
assert_eq!(self.parent, m.id);
self.parent = parent;
m
}

fn fold_method(&mut self, m: @Method) -> @Method {
let parent = self.parent;
self.parent = DUMMY_NODE_ID;
Expand All @@ -522,6 +538,12 @@ impl<'a, F: FoldOps> Folder for Ctx<'a, F> {
self.insert(block.id, EntryBlock(self.parent, block));
block
}

fn fold_lifetime(&mut self, lifetime: &Lifetime) -> Lifetime {
let lifetime = fold::noop_fold_lifetime(lifetime, self);
self.insert(lifetime.id, EntryLifetime(self.parent, @lifetime));
lifetime
}
}

pub fn map_crate<F: FoldOps>(krate: Crate, fold_ops: F) -> (Crate, Map) {
Expand Down Expand Up @@ -658,6 +680,9 @@ fn node_id_to_str(map: &Map, id: NodeId) -> ~str {
Some(NodeStructCtor(_)) => {
format!("struct_ctor {} (id={})", map.path_to_str(id), id)
}
Some(NodeLifetime(ref l)) => {
format!("lifetime {} (id={})", pprust::lifetime_to_str(*l), id)
}
None => {
format!("unknown node (id={})", id)
}
Expand Down
Loading