rustc: Add some more checks to coherence

This commit is contained in:
Patrick Walton 2012-07-11 11:22:40 -07:00
parent 1806707dcb
commit e51f259ff0

View file

@ -11,16 +11,20 @@ import middle::ty::{ty_fn, ty_trait, ty_tup, ty_var, ty_var_integral};
import middle::ty::{ty_param, ty_self, ty_constr, ty_type, ty_opaque_box};
import middle::ty::{ty_opaque_closure_ptr, ty_unboxed_vec, new_ty_hash};
import middle::ty::{subst};
import middle::typeck::infer::{infer_ctxt, mk_eqty, new_infer_ctxt};
import syntax::ast::{crate, def_id, item, item_impl, method, region_param};
import syntax::ast::{trait_ref};
import middle::typeck::infer::{infer_ctxt, mk_subty, new_infer_ctxt};
import syntax::ast::{crate, def_id, item, item_class, item_const, item_enum};
import syntax::ast::{item_fn, item_foreign_mod, item_impl, item_mac};
import syntax::ast::{item_mod, item_trait, item_ty, local_crate, method};
import syntax::ast::{node_id, region_param, rp_none, rp_self, trait_ref};
import syntax::ast_util::{def_id_of_def, new_def_hash};
import syntax::visit::{default_simple_visitor, mk_simple_visitor};
import syntax::visit::{visit_crate};
import syntax::visit::{default_simple_visitor, default_visitor};
import syntax::visit::{mk_simple_visitor, mk_vt, visit_crate, visit_item};
import syntax::visit::{visit_mod};
import util::ppaux::ty_to_str;
import dvec::{dvec, extensions};
import result::{extensions};
import std::map::hashmap;
import std::map::{hashmap, int_hash};
import uint::range;
class CoherenceInfo {
@ -43,10 +47,26 @@ class CoherenceChecker {
let inference_context: infer_ctxt;
let info: @CoherenceInfo;
// A mapping from implementations to the corresponding base type
// definition ID.
let base_type_def_ids: hashmap<node_id,def_id>;
// A set of implementations in privileged scopes; i.e. those
// implementations that are defined in the same scope as their base types.
let privileged_implementations: hashmap<node_id,()>;
// The set of types that we are currently in the privileged scope of. This
// is used while we traverse the AST while checking privileged scopes.
let privileged_types: hashmap<def_id,()>;
new(crate_context: @crate_ctxt) {
self.crate_context = crate_context;
self.inference_context = new_infer_ctxt(crate_context.tcx);
self.info = @CoherenceInfo();
self.base_type_def_ids = int_hash();
self.privileged_implementations = int_hash();
self.privileged_types = new_def_hash();
}
fn check_coherence(crate: @crate) {
@ -71,6 +91,9 @@ class CoherenceChecker {
for self.info.extension_methods.each |def_id, items| {
self.check_implementation_coherence(def_id, items);
}
// Check whether traits with base types are in privileged scopes.
self.check_privileged_scopes(crate);
}
fn check_implementation(item: @item,
@ -121,6 +144,17 @@ class CoherenceChecker {
implementation_list.push(item);
}
}
// Add the implementation to the mapping from implementation to base
// type def ID, if there is a base type for this implementation.
alt self.get_base_type_def_id(self_type.ty) {
none {
// Nothing to do.
}
some(base_type_def_id) {
self.base_type_def_ids.insert(item.id, base_type_def_id);
}
}
}
fn get_base_type(original_type: t) -> option<t> {
@ -147,6 +181,26 @@ class CoherenceChecker {
}
}
// Returns the def ID of the base type.
fn get_base_type_def_id(original_type: t) -> option<def_id> {
alt self.get_base_type(original_type) {
none {
ret none;
}
some(base_type) {
alt get(base_type).struct {
ty_enum(def_id, _) | ty_class(def_id, _) {
ret some(def_id);
}
_ {
fail "get_base_type() returned a type that wasn't an \
enum or class";
}
}
}
}
}
fn check_implementation_coherence(_trait_def_id: def_id,
implementations: @dvec<@item>) {
@ -177,7 +231,8 @@ class CoherenceChecker {
let monotype_a = self.universally_quantify_polytype(polytype_a);
let monotype_b = self.universally_quantify_polytype(polytype_b);
ret mk_eqty(self.inference_context, monotype_a, monotype_b).is_ok();
ret mk_subty(self.inference_context, monotype_a, monotype_b).is_ok()
|| mk_subty(self.inference_context, monotype_b, monotype_a).is_ok();
}
// Converts a polytype to a monotype by replacing all parameters with
@ -185,10 +240,10 @@ class CoherenceChecker {
fn universally_quantify_polytype(polytype: ty_param_bounds_and_ty) -> t {
let self_region;
alt polytype.rp {
ast::rp_none {
rp_none {
self_region = none;
}
ast::rp_self {
rp_self {
self_region = some(self.inference_context.next_region_var())
}
};
@ -220,6 +275,139 @@ class CoherenceChecker {
}
}
}
// Privileged scope checking
fn check_privileged_scopes(crate: @crate) {
visit_crate(*crate, (), mk_vt(@{
visit_item: |item, _context, visitor| {
alt item.node {
item_mod(module) {
// First, gather up all privileged types.
let privileged_types =
self.gather_privileged_types(module.items);
for privileged_types.each |privileged_type| {
#debug("(checking privileged scopes) entering \
privileged scope of %d:%d",
privileged_type.crate,
privileged_type.node);
self.privileged_types.insert(privileged_type, ());
}
// Then visit the module items.
visit_mod(module, item.span, item.id, (), visitor);
// Finally, remove privileged types from the map.
for privileged_types.each |privileged_type| {
self.privileged_types.remove(privileged_type);
}
}
item_impl(_, _, optional_trait_ref, _, _) {
alt self.base_type_def_ids.find(item.id) {
none {
// Nothing to do.
}
some(base_type_def_id) {
// Check to see whether the implementation is
// in the scope of its base type.
let privileged_types = &self.privileged_types;
if privileged_types.
contains_key(base_type_def_id) {
// Record that this implementation is OK.
self.privileged_implementations.insert
(item.id, ());
} else {
// This implementation is not in scope of
// its base type. This still might be OK
// if the trait is defined in the same
// crate.
alt optional_trait_ref {
none {
// There is no trait to implement,
// so this is an error.
let session =
self.crate_context.tcx.sess;
session.span_warn(item.span,
"cannot \
implement \
inherent \
methods for a \
type outside \
the scope the \
type was \
defined in; \
define and \
implement a \
trait \
instead");
}
some(trait_ref) {
// This is OK if and only if the
// trait was defined in this
// crate.
let def_map = self.crate_context
.tcx.def_map;
let trait_def =
def_map.get(trait_ref.id);
let trait_id =
def_id_of_def(trait_def);
if trait_id.crate != local_crate {
let session = self
.crate_context.tcx.sess;
session.span_warn(item.span,
"cannot \
provide \
an \
extension \
implement\
ation \
for a \
trait not \
defined \
in this \
crate");
}
}
}
}
}
}
visit_item(item, (), visitor);
}
_ {
visit_item(item, (), visitor);
}
}
}
with *default_visitor()
}));
}
fn gather_privileged_types(items: ~[@item]) -> @dvec<def_id> {
let results = @dvec();
for items.each |item| {
alt item.node {
item_class(*) | item_enum(*) {
results.push(local_def(item.id));
}
item_const(*) | item_fn(*) | item_mod(*) |
item_foreign_mod(*) | item_ty(*) | item_trait(*) |
item_impl(*) | item_mac(*) {
// Nothing to do.
}
}
}
ret results;
}
}
fn check_coherence(crate_context: @crate_ctxt, crate: @crate) {