diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/rustc/middle/typeck/coherence.rs | 206 |
1 files changed, 197 insertions, 9 deletions
diff --git a/src/rustc/middle/typeck/coherence.rs b/src/rustc/middle/typeck/coherence.rs index 365d945fa3d..09dbd77e9bc 100644 --- a/src/rustc/middle/typeck/coherence.rs +++ b/src/rustc/middle/typeck/coherence.rs @@ -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) { |
