Skip to content

Commit

Permalink
librustc: Enforce cross-crate method privacy
Browse files Browse the repository at this point in the history
  • Loading branch information
pcwalton committed Feb 28, 2013
1 parent 09a2b4e commit 2859c1a
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 27 deletions.
3 changes: 1 addition & 2 deletions doc/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -2304,11 +2304,10 @@ mod farm {
farmer: Human
}
// Note - visibility modifiers on impls currently have no effect
impl Farm {
priv fn feed_chickens(&self) { ... }
priv fn feed_cows(&self) { ... }
fn add_chicken(&self, c: Chicken) { ... }
pub fn add_chicken(&self, c: Chicken) { ... }
}
pub fn feed_animals(farm: &Farm) {
Expand Down
1 change: 1 addition & 0 deletions src/librustc/metadata/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ pub const tag_lang_items_item_node_id: uint = 0x75;

pub const tag_item_unnamed_field: uint = 0x76;
pub const tag_items_data_item_struct_ctor: uint = 0x77;
pub const tag_items_data_item_visibility: uint = 0x78;

pub struct LinkMeta {
name: @str,
Expand Down
8 changes: 8 additions & 0 deletions src/librustc/metadata/csearch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,14 @@ pub fn struct_dtor(cstore: @mut cstore::CStore, def: ast::def_id)
let cdata = cstore::get_crate_data(cstore, def.crate);
decoder::struct_dtor(cdata, def.node)
}

pub fn get_method_visibility(cstore: @mut cstore::CStore,
def_id: ast::def_id)
-> ast::visibility {
let cdata = cstore::get_crate_data(cstore, def_id.crate);
decoder::get_method_visibility(cdata, def_id.node)
}

// Local Variables:
// mode: rust
// fill-column: 78;
Expand Down
19 changes: 17 additions & 2 deletions src/librustc/metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,16 @@ fn item_family(item: ebml::Doc) -> Family {
}
}

fn item_visibility(item: ebml::Doc) -> ast::visibility {
let visibility = reader::get_doc(item, tag_items_data_item_visibility);
match reader::doc_as_u8(visibility) as char {
'y' => ast::public,
'n' => ast::private,
'i' => ast::inherited,
_ => fail!(~"unknown visibility character"),
}
}

fn item_method_sort(item: ebml::Doc) -> char {
for reader::tagged_docs(item, tag_item_trait_method_sort) |doc| {
return str::from_bytes(reader::doc_data(doc))[0] as char;
Expand Down Expand Up @@ -860,7 +870,7 @@ pub fn get_item_attrs(cdata: cmd,
}
}

pure fn family_to_visibility(family: Family) -> ast::visibility {
pure fn struct_field_family_to_visibility(family: Family) -> ast::visibility {
match family {
PublicField => ast::public,
PrivateField => ast::private,
Expand All @@ -883,7 +893,7 @@ pub fn get_struct_fields(intr: @ident_interner, cdata: cmd, id: ast::node_id)
result.push(ty::field_ty {
ident: name,
id: did, vis:
family_to_visibility(f),
struct_field_family_to_visibility(f),
mutability: mt,
});
}
Expand All @@ -900,6 +910,11 @@ pub fn get_struct_fields(intr: @ident_interner, cdata: cmd, id: ast::node_id)
result
}

pub fn get_method_visibility(cdata: cmd, id: ast::node_id)
-> ast::visibility {
item_visibility(lookup_item(id, cdata.data))
}

fn family_has_type_params(fam: Family) -> bool {
match fam {
Const | ForeignType | Mod | ForeignMod | PublicField | PrivateField
Expand Down
63 changes: 54 additions & 9 deletions src/librustc/metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,14 +383,26 @@ fn encode_info_for_mod(ecx: @EncodeContext, ebml_w: writer::Encoder,
ebml_w.end_tag();
}

fn encode_visibility(ebml_w: writer::Encoder, visibility: visibility) {
fn encode_struct_field_family(ebml_w: writer::Encoder,
visibility: visibility) {
encode_family(ebml_w, match visibility {
public => 'g',
private => 'j',
inherited => 'N'
});
}

fn encode_visibility(ebml_w: writer::Encoder, visibility: visibility) {
ebml_w.start_tag(tag_items_data_item_visibility);
let ch = match visibility {
public => 'y',
private => 'n',
inherited => 'i',
};
ebml_w.wr_str(str::from_char(ch));
ebml_w.end_tag();
}

fn encode_self_type(ebml_w: writer::Encoder, self_type: ast::self_ty_) {
ebml_w.start_tag(tag_item_trait_method_self_ty);

Expand Down Expand Up @@ -456,7 +468,7 @@ fn encode_info_for_struct(ecx: @EncodeContext, ebml_w: writer::Encoder,
ebml_w.start_tag(tag_items_data_item);
debug!("encode_info_for_struct: doing %s %d",
*tcx.sess.str_of(nm), id);
encode_visibility(ebml_w, vis);
encode_struct_field_family(ebml_w, vis);
encode_name(ecx, ebml_w, nm);
encode_path(ecx, ebml_w, path, ast_map::path_name(nm));
encode_type(ecx, ebml_w, node_id_to_type(tcx, id));
Expand Down Expand Up @@ -525,6 +537,7 @@ fn encode_info_for_method(ecx: @EncodeContext,
should_inline: bool,
parent_id: node_id,
m: @method,
parent_visibility: ast::visibility,
owner_generics: &ast::Generics,
method_generics: &ast::Generics) {
debug!("encode_info_for_method: %d %s %u %u", m.id,
Expand All @@ -533,6 +546,7 @@ fn encode_info_for_method(ecx: @EncodeContext,
method_generics.ty_params.len());
ebml_w.start_tag(tag_items_data_item);
encode_def_id(ebml_w, local_def(m.id));

match m.self_ty.node {
ast::sty_static => {
encode_family(ebml_w, purity_static_method_family(m.purity));
Expand All @@ -550,6 +564,14 @@ fn encode_info_for_method(ecx: @EncodeContext,
encode_name(ecx, ebml_w, m.ident);
encode_path(ecx, ebml_w, impl_path, ast_map::path_name(m.ident));
encode_self_type(ebml_w, m.self_ty.node);

// Combine parent visibility and this visibility.
let visibility = match m.vis {
ast::inherited => parent_visibility,
vis => vis,
};
encode_visibility(ebml_w, visibility);

if len > 0u || should_inline {
(ecx.encode_inlined_item)(
ecx, ebml_w, impl_path,
Expand All @@ -568,6 +590,7 @@ fn purity_fn_family(p: purity) -> char {
extern_fn => 'e'
}
}

fn purity_static_method_family(p: purity) -> char {
match p {
unsafe_fn => 'U',
Expand Down Expand Up @@ -757,7 +780,7 @@ fn encode_info_for_item(ecx: @EncodeContext, ebml_w: writer::Encoder,
match f.node.kind {
named_field(ident, _, vis) => {
ebml_w.start_tag(tag_item_field);
encode_visibility(ebml_w, vis);
encode_struct_field_family(ebml_w, vis);
encode_name(ecx, ebml_w, ident);
encode_def_id(ebml_w, local_def(f.node.id));
ebml_w.end_tag();
Expand Down Expand Up @@ -808,12 +831,28 @@ fn encode_info_for_item(ecx: @EncodeContext, ebml_w: writer::Encoder,
let mut impl_path = vec::append(~[], path);
impl_path += ~[ast_map::path_name(item.ident)];

// If there is a trait reference, treat the methods as always public.
// This is to work around some incorrect behavior in privacy checking:
// when the method belongs to a trait, it should acquire the privacy
// from the trait, not the impl. Forcing the visibility to be public
// makes things sorta work.
let parent_visibility = if opt_trait.is_some() {
ast::public
} else {
item.vis
};

for methods.each |m| {
index.push(entry {val: m.id, pos: ebml_w.writer.tell()});
encode_info_for_method(ecx, ebml_w, impl_path,
encode_info_for_method(ecx,
ebml_w,
impl_path,
should_inline(m.attrs),
item.id, *m,
generics, &m.generics);
item.id,
*m,
parent_visibility,
generics,
&m.generics);
}
}
item_trait(ref generics, ref traits, ref ms) => {
Expand Down Expand Up @@ -902,9 +941,15 @@ fn encode_info_for_item(ecx: @EncodeContext, ebml_w: writer::Encoder,
// of provided methods. I am not sure why this is. -ndm
let owner_generics = ast_util::empty_generics();

encode_info_for_method(ecx, ebml_w, /*bad*/copy path,
true, item.id, *m,
&owner_generics, &m.generics);
encode_info_for_method(ecx,
ebml_w,
/*bad*/copy path,
true,
item.id,
*m,
item.vis,
&owner_generics,
&m.generics);
}
}
item_mac(*) => fail!(~"item macros unimplemented")
Expand Down
35 changes: 26 additions & 9 deletions src/librustc/middle/privacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@

use core::prelude::*;

use metadata::csearch;
use middle::ty::{ty_struct, ty_enum};
use middle::ty;
use middle::typeck::{method_map, method_origin, method_param, method_self,
method_super};
use middle::typeck::{method_map, method_origin, method_param, method_self};
use middle::typeck::{method_super};
use middle::typeck::{method_static, method_trait};

use core::dvec::DVec;
Expand Down Expand Up @@ -100,8 +101,10 @@ pub fn check_crate(tcx: ty::ctxt,
};

// Checks that a private method is in scope.
let check_method: @fn(span: span, origin: &method_origin) =
|span, origin| {
let check_method: @fn(span: span,
origin: &method_origin,
ident: ast::ident) =
|span, origin, ident| {
match *origin {
method_static(method_id) => {
if method_id.crate == local_crate {
Expand All @@ -110,6 +113,8 @@ pub fn check_crate(tcx: ty::ctxt,
let mut is_private = false;
if method.vis == private {
is_private = true;
} else if method.vis == public {
is_private = false;
} else {
// Look up the enclosing impl.
if impl_id.crate != local_crate {
Expand All @@ -121,7 +126,7 @@ pub fn check_crate(tcx: ty::ctxt,
match tcx.items.find(&impl_id.node) {
Some(node_item(item, _)) => {
match item.node {
item_impl(_, None, _, _)
item_impl(_, None, _, _)
if item.vis != public => {
is_private = true;
}
Expand Down Expand Up @@ -165,7 +170,15 @@ pub fn check_crate(tcx: ty::ctxt,
}
}
} else {
// FIXME #4732: External crates.
let visibility =
csearch::get_method_visibility(tcx.sess.cstore,
method_id);
if visibility != public {
tcx.sess.span_err(span,
fmt!("method `%s` is private",
*tcx.sess.parse_sess.interner
.get(ident)));
}
}
}
method_param(method_param {
Expand Down Expand Up @@ -264,14 +277,16 @@ pub fn check_crate(tcx: ty::ctxt,
Some(ref entry) => {
debug!("(privacy checking) checking \
impl method");
check_method(expr.span, &(*entry).origin);
check_method(expr.span,
&entry.origin,
ident);
}
}
}
_ => {}
}
}
expr_method_call(base, _, _, _, _) => {
expr_method_call(base, ident, _, _, _) => {
// Ditto
match ty::get(ty::type_autoderef(tcx, ty::expr_ty(tcx,
base))).sty {
Expand All @@ -287,7 +302,9 @@ pub fn check_crate(tcx: ty::ctxt,
Some(ref entry) => {
debug!("(privacy checking) checking \
impl method");
check_method(expr.span, &(*entry).origin);
check_method(expr.span,
&entry.origin,
ident);
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/libstd/oldmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ pub mod chained {
}
self.chains = new_chains;
}
}

pub impl<K:Eq + IterBytes + Hash,V> T<K, V> {
pure fn each_entry(blk: fn(@Entry<K,V>) -> bool) {
// n.b. we can't use vec::iter() here because self.chains
// is stored in a mutable location.
Expand Down
4 changes: 2 additions & 2 deletions src/test/auxiliary/cci_class_5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@

pub mod kitties {
pub struct cat {
priv mut meows : uint,
priv meows : uint,
how_hungry : int,
}

pub impl cat {
priv fn nap() { for uint::range(1, 10000u) |_i|{}}
priv fn nap(&self) { for uint::range(1, 10000u) |_i|{}}
}

pub fn cat(in_x : uint, in_y : int) -> cat {
Expand Down
4 changes: 1 addition & 3 deletions src/test/compile-fail/private-method-cross-crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern:attempted access of field `nap` on type
// xfail-test Cross-crate impl method privacy doesn't work
// xfail-fast
// aux-build:cci_class_5.rs
extern mod cci_class_5;
use cci_class_5::kitties::*;

fn main() {
let nyan : cat = cat(52, 99);
nyan.nap();
nyan.nap(); //~ ERROR method `nap` is private
}

5 comments on commit 2859c1a

@bors
Copy link
Contributor

@bors bors commented on 2859c1a Feb 28, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from pcwalton
at pcwalton@2859c1a

@bors
Copy link
Contributor

@bors bors commented on 2859c1a Feb 28, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging pcwalton/rust/method-privacy = 2859c1a into auto

@bors
Copy link
Contributor

@bors bors commented on 2859c1a Feb 28, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pcwalton/rust/method-privacy = 2859c1a merged ok, testing candidate = e1d3a4f

@bors
Copy link
Contributor

@bors bors commented on 2859c1a Feb 28, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bors
Copy link
Contributor

@bors bors commented on 2859c1a Feb 28, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding incoming to auto = e1d3a4f

Please sign in to comment.