Skip to content

Commit 3099451

Browse files
committed
Add result sanity check to is_to_be_inferred.
This version of `is_to_be_inferred` double-checks the result from `inferred_map` by querying the `named_region_map` and `ast_map` and then asserts that the `inferred_map` state is consistent with its own findings. (See issue 13261 for further discussion of the approaches).
1 parent 364d82e commit 3099451

File tree

1 file changed

+69
-1
lines changed

1 file changed

+69
-1
lines changed

src/librustc/middle/typeck/variance.rs

+69-1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ use arena::Arena;
198198
use middle::ty;
199199
use std::fmt;
200200
use syntax::ast;
201+
use syntax::ast_map;
201202
use syntax::ast_util;
202203
use syntax::owned_slice::OwnedSlice;
203204
use syntax::visit;
@@ -517,6 +518,13 @@ impl<'a> Visitor<()> for ConstraintContext<'a> {
517518
}
518519
}
519520

521+
/// Is `param_id` a lifetime according to `map`?
522+
fn is_lifetime(map: &ast_map::Map, param_id: ast::NodeId) -> bool {
523+
match map.find(param_id) {
524+
Some(ast_map::NodeLifetime(..)) => true, _ => false
525+
}
526+
}
527+
520528
impl<'a> ConstraintContext<'a> {
521529
fn tcx(&self) -> &'a ty::ctxt {
522530
self.terms_cx.tcx
@@ -533,8 +541,68 @@ impl<'a> ConstraintContext<'a> {
533541
}
534542
}
535543

544+
fn find_binding_for_lifetime(&self, param_id: ast::NodeId) -> ast::NodeId {
545+
let tcx = self.terms_cx.tcx;
546+
assert!(is_lifetime(&tcx.map, param_id));
547+
match tcx.named_region_map.find(&param_id) {
548+
Some(&ast::DefEarlyBoundRegion(_, lifetime_decl_id))
549+
=> lifetime_decl_id,
550+
Some(_) => fail!("should not encounter non early-bound cases"),
551+
552+
// The lookup should only fail when `param_id` is
553+
// itself a lifetime binding: use it as the decl_id.
554+
None => param_id,
555+
}
556+
557+
}
558+
559+
/// Is `param_id` a type parameter for which we infer variance?
536560
fn is_to_be_inferred(&self, param_id: ast::NodeId) -> bool {
537-
self.terms_cx.inferred_map.contains_key(&param_id)
561+
let result = self.terms_cx.inferred_map.contains_key(&param_id);
562+
563+
// To safe-guard against invalid inferred_map constructions,
564+
// double-check if variance is inferred at some use of a type
565+
// parameter (by inspecting parent of its binding declaration
566+
// to see if it is introduced by a type or by a fn/impl).
567+
568+
let check_result = |this:&ConstraintContext| -> bool {
569+
let tcx = this.terms_cx.tcx;
570+
let decl_id = this.find_binding_for_lifetime(param_id);
571+
// Currently only called on lifetimes; double-checking that.
572+
assert!(is_lifetime(&tcx.map, param_id));
573+
let parent_id = tcx.map.get_parent(decl_id);
574+
let parent = tcx.map.find(parent_id).unwrap_or_else(
575+
|| fail!("tcx.map missing entry for id: {}", parent_id));
576+
577+
let is_inferred;
578+
macro_rules! cannot_happen { () => { {
579+
fail!("invalid parent: {:s} for {:s}",
580+
tcx.map.node_to_str(parent_id),
581+
tcx.map.node_to_str(param_id));
582+
} } }
583+
584+
match parent {
585+
ast_map::NodeItem(p) => {
586+
match p.node {
587+
ast::ItemTy(..) |
588+
ast::ItemEnum(..) |
589+
ast::ItemStruct(..) |
590+
ast::ItemTrait(..) => is_inferred = true,
591+
ast::ItemFn(..) => is_inferred = false,
592+
_ => cannot_happen!(),
593+
}
594+
}
595+
ast_map::NodeTraitMethod(..) => is_inferred = false,
596+
ast_map::NodeMethod(_) => is_inferred = false,
597+
_ => cannot_happen!(),
598+
}
599+
600+
return is_inferred;
601+
};
602+
603+
assert_eq!(result, check_result(self));
604+
605+
return result;
538606
}
539607

540608
fn declared_variance(&self,

0 commit comments

Comments
 (0)