Skip to content

Commit 2859c1a

Browse files
committed
librustc: Enforce cross-crate method privacy
1 parent 09a2b4e commit 2859c1a

File tree

9 files changed

+112
-27
lines changed

9 files changed

+112
-27
lines changed

doc/tutorial.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -2304,11 +2304,10 @@ mod farm {
23042304
farmer: Human
23052305
}
23062306
2307-
// Note - visibility modifiers on impls currently have no effect
23082307
impl Farm {
23092308
priv fn feed_chickens(&self) { ... }
23102309
priv fn feed_cows(&self) { ... }
2311-
fn add_chicken(&self, c: Chicken) { ... }
2310+
pub fn add_chicken(&self, c: Chicken) { ... }
23122311
}
23132312
23142313
pub fn feed_animals(farm: &Farm) {

src/librustc/metadata/common.rs

+1
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ pub const tag_lang_items_item_node_id: uint = 0x75;
155155

156156
pub const tag_item_unnamed_field: uint = 0x76;
157157
pub const tag_items_data_item_struct_ctor: uint = 0x77;
158+
pub const tag_items_data_item_visibility: uint = 0x78;
158159

159160
pub struct LinkMeta {
160161
name: @str,

src/librustc/metadata/csearch.rs

+8
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,14 @@ pub fn struct_dtor(cstore: @mut cstore::CStore, def: ast::def_id)
234234
let cdata = cstore::get_crate_data(cstore, def.crate);
235235
decoder::struct_dtor(cdata, def.node)
236236
}
237+
238+
pub fn get_method_visibility(cstore: @mut cstore::CStore,
239+
def_id: ast::def_id)
240+
-> ast::visibility {
241+
let cdata = cstore::get_crate_data(cstore, def_id.crate);
242+
decoder::get_method_visibility(cdata, def_id.node)
243+
}
244+
237245
// Local Variables:
238246
// mode: rust
239247
// fill-column: 78;

src/librustc/metadata/decoder.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,16 @@ fn item_family(item: ebml::Doc) -> Family {
151151
}
152152
}
153153

154+
fn item_visibility(item: ebml::Doc) -> ast::visibility {
155+
let visibility = reader::get_doc(item, tag_items_data_item_visibility);
156+
match reader::doc_as_u8(visibility) as char {
157+
'y' => ast::public,
158+
'n' => ast::private,
159+
'i' => ast::inherited,
160+
_ => fail!(~"unknown visibility character"),
161+
}
162+
}
163+
154164
fn item_method_sort(item: ebml::Doc) -> char {
155165
for reader::tagged_docs(item, tag_item_trait_method_sort) |doc| {
156166
return str::from_bytes(reader::doc_data(doc))[0] as char;
@@ -860,7 +870,7 @@ pub fn get_item_attrs(cdata: cmd,
860870
}
861871
}
862872

863-
pure fn family_to_visibility(family: Family) -> ast::visibility {
873+
pure fn struct_field_family_to_visibility(family: Family) -> ast::visibility {
864874
match family {
865875
PublicField => ast::public,
866876
PrivateField => ast::private,
@@ -883,7 +893,7 @@ pub fn get_struct_fields(intr: @ident_interner, cdata: cmd, id: ast::node_id)
883893
result.push(ty::field_ty {
884894
ident: name,
885895
id: did, vis:
886-
family_to_visibility(f),
896+
struct_field_family_to_visibility(f),
887897
mutability: mt,
888898
});
889899
}
@@ -900,6 +910,11 @@ pub fn get_struct_fields(intr: @ident_interner, cdata: cmd, id: ast::node_id)
900910
result
901911
}
902912

913+
pub fn get_method_visibility(cdata: cmd, id: ast::node_id)
914+
-> ast::visibility {
915+
item_visibility(lookup_item(id, cdata.data))
916+
}
917+
903918
fn family_has_type_params(fam: Family) -> bool {
904919
match fam {
905920
Const | ForeignType | Mod | ForeignMod | PublicField | PrivateField

src/librustc/metadata/encoder.rs

+54-9
Original file line numberDiff line numberDiff line change
@@ -383,14 +383,26 @@ fn encode_info_for_mod(ecx: @EncodeContext, ebml_w: writer::Encoder,
383383
ebml_w.end_tag();
384384
}
385385

386-
fn encode_visibility(ebml_w: writer::Encoder, visibility: visibility) {
386+
fn encode_struct_field_family(ebml_w: writer::Encoder,
387+
visibility: visibility) {
387388
encode_family(ebml_w, match visibility {
388389
public => 'g',
389390
private => 'j',
390391
inherited => 'N'
391392
});
392393
}
393394

395+
fn encode_visibility(ebml_w: writer::Encoder, visibility: visibility) {
396+
ebml_w.start_tag(tag_items_data_item_visibility);
397+
let ch = match visibility {
398+
public => 'y',
399+
private => 'n',
400+
inherited => 'i',
401+
};
402+
ebml_w.wr_str(str::from_char(ch));
403+
ebml_w.end_tag();
404+
}
405+
394406
fn encode_self_type(ebml_w: writer::Encoder, self_type: ast::self_ty_) {
395407
ebml_w.start_tag(tag_item_trait_method_self_ty);
396408

@@ -456,7 +468,7 @@ fn encode_info_for_struct(ecx: @EncodeContext, ebml_w: writer::Encoder,
456468
ebml_w.start_tag(tag_items_data_item);
457469
debug!("encode_info_for_struct: doing %s %d",
458470
*tcx.sess.str_of(nm), id);
459-
encode_visibility(ebml_w, vis);
471+
encode_struct_field_family(ebml_w, vis);
460472
encode_name(ecx, ebml_w, nm);
461473
encode_path(ecx, ebml_w, path, ast_map::path_name(nm));
462474
encode_type(ecx, ebml_w, node_id_to_type(tcx, id));
@@ -525,6 +537,7 @@ fn encode_info_for_method(ecx: @EncodeContext,
525537
should_inline: bool,
526538
parent_id: node_id,
527539
m: @method,
540+
parent_visibility: ast::visibility,
528541
owner_generics: &ast::Generics,
529542
method_generics: &ast::Generics) {
530543
debug!("encode_info_for_method: %d %s %u %u", m.id,
@@ -533,6 +546,7 @@ fn encode_info_for_method(ecx: @EncodeContext,
533546
method_generics.ty_params.len());
534547
ebml_w.start_tag(tag_items_data_item);
535548
encode_def_id(ebml_w, local_def(m.id));
549+
536550
match m.self_ty.node {
537551
ast::sty_static => {
538552
encode_family(ebml_w, purity_static_method_family(m.purity));
@@ -550,6 +564,14 @@ fn encode_info_for_method(ecx: @EncodeContext,
550564
encode_name(ecx, ebml_w, m.ident);
551565
encode_path(ecx, ebml_w, impl_path, ast_map::path_name(m.ident));
552566
encode_self_type(ebml_w, m.self_ty.node);
567+
568+
// Combine parent visibility and this visibility.
569+
let visibility = match m.vis {
570+
ast::inherited => parent_visibility,
571+
vis => vis,
572+
};
573+
encode_visibility(ebml_w, visibility);
574+
553575
if len > 0u || should_inline {
554576
(ecx.encode_inlined_item)(
555577
ecx, ebml_w, impl_path,
@@ -568,6 +590,7 @@ fn purity_fn_family(p: purity) -> char {
568590
extern_fn => 'e'
569591
}
570592
}
593+
571594
fn purity_static_method_family(p: purity) -> char {
572595
match p {
573596
unsafe_fn => 'U',
@@ -757,7 +780,7 @@ fn encode_info_for_item(ecx: @EncodeContext, ebml_w: writer::Encoder,
757780
match f.node.kind {
758781
named_field(ident, _, vis) => {
759782
ebml_w.start_tag(tag_item_field);
760-
encode_visibility(ebml_w, vis);
783+
encode_struct_field_family(ebml_w, vis);
761784
encode_name(ecx, ebml_w, ident);
762785
encode_def_id(ebml_w, local_def(f.node.id));
763786
ebml_w.end_tag();
@@ -808,12 +831,28 @@ fn encode_info_for_item(ecx: @EncodeContext, ebml_w: writer::Encoder,
808831
let mut impl_path = vec::append(~[], path);
809832
impl_path += ~[ast_map::path_name(item.ident)];
810833
834+
// If there is a trait reference, treat the methods as always public.
835+
// This is to work around some incorrect behavior in privacy checking:
836+
// when the method belongs to a trait, it should acquire the privacy
837+
// from the trait, not the impl. Forcing the visibility to be public
838+
// makes things sorta work.
839+
let parent_visibility = if opt_trait.is_some() {
840+
ast::public
841+
} else {
842+
item.vis
843+
};
844+
811845
for methods.each |m| {
812846
index.push(entry {val: m.id, pos: ebml_w.writer.tell()});
813-
encode_info_for_method(ecx, ebml_w, impl_path,
847+
encode_info_for_method(ecx,
848+
ebml_w,
849+
impl_path,
814850
should_inline(m.attrs),
815-
item.id, *m,
816-
generics, &m.generics);
851+
item.id,
852+
*m,
853+
parent_visibility,
854+
generics,
855+
&m.generics);
817856
}
818857
}
819858
item_trait(ref generics, ref traits, ref ms) => {
@@ -902,9 +941,15 @@ fn encode_info_for_item(ecx: @EncodeContext, ebml_w: writer::Encoder,
902941
// of provided methods. I am not sure why this is. -ndm
903942
let owner_generics = ast_util::empty_generics();
904943
905-
encode_info_for_method(ecx, ebml_w, /*bad*/copy path,
906-
true, item.id, *m,
907-
&owner_generics, &m.generics);
944+
encode_info_for_method(ecx,
945+
ebml_w,
946+
/*bad*/copy path,
947+
true,
948+
item.id,
949+
*m,
950+
item.vis,
951+
&owner_generics,
952+
&m.generics);
908953
}
909954
}
910955
item_mac(*) => fail!(~"item macros unimplemented")

src/librustc/middle/privacy.rs

+26-9
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414

1515
use core::prelude::*;
1616

17+
use metadata::csearch;
1718
use middle::ty::{ty_struct, ty_enum};
1819
use middle::ty;
19-
use middle::typeck::{method_map, method_origin, method_param, method_self,
20-
method_super};
20+
use middle::typeck::{method_map, method_origin, method_param, method_self};
21+
use middle::typeck::{method_super};
2122
use middle::typeck::{method_static, method_trait};
2223

2324
use core::dvec::DVec;
@@ -100,8 +101,10 @@ pub fn check_crate(tcx: ty::ctxt,
100101
};
101102

102103
// Checks that a private method is in scope.
103-
let check_method: @fn(span: span, origin: &method_origin) =
104-
|span, origin| {
104+
let check_method: @fn(span: span,
105+
origin: &method_origin,
106+
ident: ast::ident) =
107+
|span, origin, ident| {
105108
match *origin {
106109
method_static(method_id) => {
107110
if method_id.crate == local_crate {
@@ -110,6 +113,8 @@ pub fn check_crate(tcx: ty::ctxt,
110113
let mut is_private = false;
111114
if method.vis == private {
112115
is_private = true;
116+
} else if method.vis == public {
117+
is_private = false;
113118
} else {
114119
// Look up the enclosing impl.
115120
if impl_id.crate != local_crate {
@@ -121,7 +126,7 @@ pub fn check_crate(tcx: ty::ctxt,
121126
match tcx.items.find(&impl_id.node) {
122127
Some(node_item(item, _)) => {
123128
match item.node {
124-
item_impl(_, None, _, _)
129+
item_impl(_, None, _, _)
125130
if item.vis != public => {
126131
is_private = true;
127132
}
@@ -165,7 +170,15 @@ pub fn check_crate(tcx: ty::ctxt,
165170
}
166171
}
167172
} else {
168-
// FIXME #4732: External crates.
173+
let visibility =
174+
csearch::get_method_visibility(tcx.sess.cstore,
175+
method_id);
176+
if visibility != public {
177+
tcx.sess.span_err(span,
178+
fmt!("method `%s` is private",
179+
*tcx.sess.parse_sess.interner
180+
.get(ident)));
181+
}
169182
}
170183
}
171184
method_param(method_param {
@@ -264,14 +277,16 @@ pub fn check_crate(tcx: ty::ctxt,
264277
Some(ref entry) => {
265278
debug!("(privacy checking) checking \
266279
impl method");
267-
check_method(expr.span, &(*entry).origin);
280+
check_method(expr.span,
281+
&entry.origin,
282+
ident);
268283
}
269284
}
270285
}
271286
_ => {}
272287
}
273288
}
274-
expr_method_call(base, _, _, _, _) => {
289+
expr_method_call(base, ident, _, _, _) => {
275290
// Ditto
276291
match ty::get(ty::type_autoderef(tcx, ty::expr_ty(tcx,
277292
base))).sty {
@@ -287,7 +302,9 @@ pub fn check_crate(tcx: ty::ctxt,
287302
Some(ref entry) => {
288303
debug!("(privacy checking) checking \
289304
impl method");
290-
check_method(expr.span, &(*entry).origin);
305+
check_method(expr.span,
306+
&entry.origin,
307+
ident);
291308
}
292309
}
293310
}

src/libstd/oldmap.rs

+2
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,9 @@ pub mod chained {
134134
}
135135
self.chains = new_chains;
136136
}
137+
}
137138

139+
pub impl<K:Eq + IterBytes + Hash,V> T<K, V> {
138140
pure fn each_entry(blk: fn(@Entry<K,V>) -> bool) {
139141
// n.b. we can't use vec::iter() here because self.chains
140142
// is stored in a mutable location.

src/test/auxiliary/cci_class_5.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010

1111
pub mod kitties {
1212
pub struct cat {
13-
priv mut meows : uint,
13+
priv meows : uint,
1414
how_hungry : int,
1515
}
1616

1717
pub impl cat {
18-
priv fn nap() { for uint::range(1, 10000u) |_i|{}}
18+
priv fn nap(&self) { for uint::range(1, 10000u) |_i|{}}
1919
}
2020

2121
pub fn cat(in_x : uint, in_y : int) -> cat {

src/test/compile-fail/private-method-cross-crate.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

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

1816
fn main() {
1917
let nyan : cat = cat(52, 99);
20-
nyan.nap();
18+
nyan.nap(); //~ ERROR method `nap` is private
2119
}

0 commit comments

Comments
 (0)