Skip to content

Commit 6ddd011

Browse files
committed
auto merge of #9735 : alexcrichton/rust/privacy, r=cmr
This is the culmination and attempted resolution of #8215. The commits have many more details about implementation details and the consequences of this refinement. I'll point out specific locations which may be possible causes for alarm. In general, I have been very happy with how things have turned out. I'm a little sad that I couldn't remove privacy from resolve as much as I did, but I blame glob imports (although in theory even some of this can be mitigated as well).
2 parents 1320999 + 7cd6692 commit 6ddd011

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1861
-1226
lines changed

doc/rust.md

+165
Original file line numberDiff line numberDiff line change
@@ -1501,6 +1501,171 @@ is `extern "abi" fn(A1, ..., An) -> R`,
15011501
where `A1...An` are the declared types of its arguments
15021502
and `R` is the decalred return type.
15031503

1504+
## Visibility and Privacy
1505+
1506+
These two terms are often used interchangeably, and what they are attempting to
1507+
convey is the answer to the question "Can this item be used at this location?"
1508+
1509+
Rust's name resolution operates on a global hierarchy of namespaces. Each level
1510+
in the hierarchy can be thought of as some item. The items are one of those
1511+
mentioned above, but also include external crates. Declaring or defining a new
1512+
module can be thought of as inserting a new tree into the hierarchy at the
1513+
location of the definition.
1514+
1515+
To control whether interfaces can be used across modules, Rust checks each use
1516+
of an item to see whether it should be allowed or not. This is where privacy
1517+
warnings are generated, or otherwise "you used a private item of another module
1518+
and weren't allowed to."
1519+
1520+
By default, everything in rust is *private*, with two exceptions. The first
1521+
exception is that struct fields are public by default (but the struct itself is
1522+
still private by default), and the remaining exception is that enum variants in
1523+
a `pub` enum are the default visibility of the enum container itself.. You are
1524+
allowed to alter this default visibility with the `pub` keyword (or `priv`
1525+
keyword for struct fields and enum variants). When an item is declared as `pub`,
1526+
it can be thought of as being accessible to the outside world. For example:
1527+
1528+
~~~
1529+
// Declare a private struct
1530+
struct Foo;
1531+
1532+
// Declare a public struct with a private field
1533+
pub struct Bar {
1534+
priv field: int
1535+
}
1536+
1537+
// Declare a public enum with public and private variants
1538+
pub enum State {
1539+
PubliclyAccessibleState,
1540+
priv PrivatelyAccessibleState
1541+
}
1542+
~~~
1543+
1544+
With the notion of an item being either public or private, Rust allows item
1545+
accesses in two cases:
1546+
1547+
1. If an item is public, then it can be used externally through any of its
1548+
public ancestors.
1549+
2. If an item is private, it may be accessed by the current module and its
1550+
descendants.
1551+
1552+
These two cases are surprisingly powerful for creating module hierarchies
1553+
exposing public APIs while hiding internal implementation details. To help
1554+
explain, here's a few use cases and what they would entail.
1555+
1556+
* A library developer needs to expose functionality to crates which link against
1557+
their library. As a consequence of the first case, this means that anything
1558+
which is usable externally must be `pub` from the root down to the destination
1559+
item. Any private item in the chain will disallow external accesses.
1560+
1561+
* A crate needs a global available "helper module" to itself, but it doesn't
1562+
want to expose the helper module as a public API. To accomplish this, the root
1563+
of the crate's hierarchy would have a private module which then internally has
1564+
a "public api". Because the entire crate is an ancestor of the root, then the
1565+
entire local crate can access this private module through the second case.
1566+
1567+
* When writing unit tests for a module, it's often a common idiom to have an
1568+
immediate child of the module to-be-tested named `mod test`. This module could
1569+
access any items of the parent module through the second case, meaning that
1570+
internal implementation details could also be seamlessly tested from the child
1571+
module.
1572+
1573+
In the second case, it mentions that a private item "can be accessed" by the
1574+
current module and its descendants, but the exact meaning of accessing an item
1575+
depends on what the item is. Accessing a module, for example, would mean looking
1576+
inside of it (to import more items). On the other hand, accessing a function
1577+
would mean that it is invoked.
1578+
1579+
Here's an example of a program which exemplifies the three cases outlined above.
1580+
1581+
~~~
1582+
// This module is private, meaning that no external crate can access this
1583+
// module. Because it is private at the root of this current crate, however, any
1584+
// module in the crate may access any publicly visible item in this module.
1585+
mod crate_helper_module {
1586+
1587+
// This function can be used by anything in the current crate
1588+
pub fn crate_helper() {}
1589+
1590+
// This function *cannot* be used by anything else in the crate. It is not
1591+
// publicly visible outside of the `crate_helper_module`, so only this
1592+
// current module and its descendants may access it.
1593+
fn implementation_detail() {}
1594+
}
1595+
1596+
// This function is "public to the root" meaning that it's available to external
1597+
// crates linking against this one.
1598+
pub fn public_api() {}
1599+
1600+
// Similarly to 'public_api', this module is public so external crates may look
1601+
// inside of it.
1602+
pub mod submodule {
1603+
use crate_helper_module;
1604+
1605+
pub fn my_method() {
1606+
// Any item in the local crate may invoke the helper module's public
1607+
// interface through a combination of the two rules above.
1608+
crate_helper_module::crate_helper();
1609+
}
1610+
1611+
// This function is hidden to any module which is not a descendant of
1612+
// `submodule`
1613+
fn my_implementation() {}
1614+
1615+
#[cfg(test)]
1616+
mod test {
1617+
1618+
#[test]
1619+
fn test_my_implementation() {
1620+
// Because this module is a descendant of `submodule`, it's allowed
1621+
// to access private items inside of `submodule` without a privacy
1622+
// violation.
1623+
super::my_implementation();
1624+
}
1625+
}
1626+
}
1627+
1628+
# fn main() {}
1629+
~~~
1630+
1631+
For a rust program to pass the privacy checking pass, all paths must be valid
1632+
accesses given the two rules above. This includes all use statements,
1633+
expressions, types, etc.
1634+
1635+
### Re-exporting and Visibility
1636+
1637+
Rust allows publicly re-exporting items through a `pub use` directive. Because
1638+
this is a public directive, this allows the item to be used in the current
1639+
module through the rules above. It essentially allows public access into the
1640+
re-exported item. For example, this program is valid:
1641+
1642+
~~~
1643+
pub use api = self::implementation;
1644+
1645+
mod implementation {
1646+
pub fn f() {}
1647+
}
1648+
1649+
# fn main() {}
1650+
~~~
1651+
1652+
This means that any external crate referencing `implementation::f` would receive
1653+
a privacy violation, while the path `api::f` would be allowed.
1654+
1655+
When re-exporting a private item, it can be thought of as allowing the "privacy
1656+
chain" being short-circuited through the reexport instead of passing through the
1657+
namespace hierarchy as it normally would.
1658+
1659+
### Glob imports and Visibility
1660+
1661+
Currently glob imports are considered an "experimental" language feature. For
1662+
sanity purpose along with helping the implementation, glob imports will only
1663+
import public items from their destination, not private items.
1664+
1665+
> **Note:** This is subject to change, glob exports may be removed entirely or
1666+
> they could possibly import private items for a privacy error to later be
1667+
> issued if the item is used.
1668+
15041669
## Attributes
15051670

15061671
~~~~~~~~{.ebnf .gram}

doc/tutorial.md

+13-12
Original file line numberDiff line numberDiff line change
@@ -2322,19 +2322,18 @@ fn main() {
23222322

23232323
The `::farm::chicken` construct is what we call a 'path'.
23242324

2325-
Because it's starting with a `::`, it's also a 'global path',
2326-
which qualifies an item by its full path in the module hierarchy
2327-
relative to the crate root.
2325+
Because it's starting with a `::`, it's also a 'global path', which qualifies
2326+
an item by its full path in the module hierarchy relative to the crate root.
23282327

2329-
If the path were to start with a regular identifier, like `farm::chicken`, it would be
2330-
a 'local path' instead. We'll get to them later.
2328+
If the path were to start with a regular identifier, like `farm::chicken`, it
2329+
would be a 'local path' instead. We'll get to them later.
23312330

2332-
Now, if you actually tried to compile this code example, you'll notice
2333-
that you get a `unresolved name: 'farm::chicken'` error. That's because per default,
2334-
items (`fn`, `struct`, `static`, `mod`, ...) are only visible inside the module
2335-
they are defined in.
2331+
Now, if you actually tried to compile this code example, you'll notice that you
2332+
get a `function 'chicken' is private` error. That's because by default, items
2333+
(`fn`, `struct`, `static`, `mod`, ...) are private.
23362334

2337-
To make them visible outside their containing modules, you need to mark them _public_ with `pub`:
2335+
To make them visible outside their containing modules, you need to mark them
2336+
_public_ with `pub`:
23382337

23392338
~~~~
23402339
mod farm {
@@ -2356,7 +2355,8 @@ Rust doesn't support encapsulation: both struct fields and methods can
23562355
be private. But this encapsulation is at the module level, not the
23572356
struct level.
23582357

2359-
For convenience, fields are _public_ by default, and can be made _private_ with the `priv` keyword:
2358+
For convenience, fields are _public_ by default, and can be made _private_ with
2359+
the `priv` keyword:
23602360

23612361
~~~
23622362
mod farm {
@@ -2393,7 +2393,8 @@ fn main() {
23932393
# fn make_me_a_chicken() -> farm::Chicken { 0 }
23942394
~~~
23952395

2396-
> ***Note:*** Visibility rules are currently buggy and not fully defined, you might have to add or remove `pub` along a path until it works.
2396+
Exact details and specifications about visibility rules can be found in the Rust
2397+
manual.
23972398

23982399
## Files and modules
23992400

src/libextra/container.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub trait Deque<T> : Mutable {
4040
}
4141

4242
#[cfg(test)]
43-
mod bench {
43+
pub mod bench {
4444
use std::container::MutableMap;
4545
use std::{vec, rand};
4646
use std::rand::Rng;

src/libextra/crypto/cryptoutil.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ impl <T: FixedBuffer> StandardPadding for T {
346346

347347

348348
#[cfg(test)]
349-
mod test {
349+
pub mod test {
350350
use std::rand::{IsaacRng, Rng};
351351
use std::vec;
352352

src/libextra/stats.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ pub trait Stats {
101101

102102
/// Extracted collection of all the summary statistics of a sample set.
103103
#[deriving(Clone, Eq)]
104-
struct Summary {
104+
#[allow(missing_doc)]
105+
pub struct Summary {
105106
sum: f64,
106107
min: f64,
107108
max: f64,

src/librustc/driver/driver.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,6 @@ pub fn phase_2_configure_and_expand(sess: Session,
199199

200200
pub struct CrateAnalysis {
201201
exp_map2: middle::resolve::ExportMap2,
202-
exported_items: @middle::privacy::ExportedItems,
203202
ty_cx: ty::ctxt,
204203
maps: astencode::Maps,
205204
reachable: @mut HashSet<ast::NodeId>
@@ -229,7 +228,9 @@ pub fn phase_3_run_analysis_passes(sess: Session,
229228
let middle::resolve::CrateMap {
230229
def_map: def_map,
231230
exp_map2: exp_map2,
232-
trait_map: trait_map
231+
trait_map: trait_map,
232+
external_exports: external_exports,
233+
last_private_map: last_private_map
233234
} =
234235
time(time_passes, "resolution", (), |_|
235236
middle::resolve::resolve_crate(sess, lang_items, crate));
@@ -261,9 +262,10 @@ pub fn phase_3_run_analysis_passes(sess: Session,
261262
middle::check_const::check_crate(sess, crate, ast_map, def_map,
262263
method_map, ty_cx));
263264

264-
let exported_items =
265-
time(time_passes, "privacy checking", (), |_|
266-
middle::privacy::check_crate(ty_cx, &method_map, &exp_map2, crate));
265+
let maps = (external_exports, last_private_map);
266+
time(time_passes, "privacy checking", maps, |(a, b)|
267+
middle::privacy::check_crate(ty_cx, &method_map, &exp_map2,
268+
a, b, crate));
267269

268270
time(time_passes, "effect checking", (), |_|
269271
middle::effect::check_crate(ty_cx, method_map, crate));
@@ -305,7 +307,6 @@ pub fn phase_3_run_analysis_passes(sess: Session,
305307

306308
CrateAnalysis {
307309
exp_map2: exp_map2,
308-
exported_items: @exported_items,
309310
ty_cx: ty_cx,
310311
maps: astencode::Maps {
311312
root_map: root_map,

src/librustc/metadata/decoder.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -837,8 +837,9 @@ fn each_child_of_item_or_crate(intr: @ident_interner,
837837
let def_like = item_to_def_like(child_item_doc,
838838
child_def_id,
839839
cdata.cnum);
840-
callback(def_like, token::str_to_ident(name),
841-
item_visibility(child_item_doc));
840+
// These items have a public visibility because they're part of
841+
// a public re-export.
842+
callback(def_like, token::str_to_ident(name), ast::public);
842843
}
843844
}
844845

src/librustc/metadata/encoder.rs

+3-11
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ pub struct EncodeParams<'self> {
5858
diag: @mut span_handler,
5959
tcx: ty::ctxt,
6060
reexports2: middle::resolve::ExportMap2,
61-
exported_items: @middle::privacy::ExportedItems,
6261
item_symbols: &'self HashMap<ast::NodeId, ~str>,
6362
discrim_symbols: &'self HashMap<ast::NodeId, @str>,
6463
non_inlineable_statics: &'self HashSet<ast::NodeId>,
@@ -89,7 +88,6 @@ pub struct EncodeContext<'self> {
8988
tcx: ty::ctxt,
9089
stats: @mut Stats,
9190
reexports2: middle::resolve::ExportMap2,
92-
exported_items: @middle::privacy::ExportedItems,
9391
item_symbols: &'self HashMap<ast::NodeId, ~str>,
9492
discrim_symbols: &'self HashMap<ast::NodeId, @str>,
9593
non_inlineable_statics: &'self HashSet<ast::NodeId>,
@@ -625,6 +623,7 @@ fn encode_info_for_mod(ecx: &EncodeContext,
625623
}
626624

627625
encode_path(ecx, ebml_w, path, ast_map::path_mod(name));
626+
encode_visibility(ebml_w, vis);
628627

629628
// Encode the reexports of this module, if this module is public.
630629
if vis == public {
@@ -1277,12 +1276,7 @@ fn my_visit_item(i:@item, items: ast_map::map, ebml_w:&writer::Encoder,
12771276
let mut ebml_w = ebml_w.clone();
12781277
// See above
12791278
let ecx : &EncodeContext = unsafe { cast::transmute(ecx_ptr) };
1280-
let vis = if ecx.exported_items.contains(&i.id) {
1281-
ast::public
1282-
} else {
1283-
ast::inherited
1284-
};
1285-
encode_info_for_item(ecx, &mut ebml_w, i, index, *pt, vis);
1279+
encode_info_for_item(ecx, &mut ebml_w, i, index, *pt, i.vis);
12861280
}
12871281
_ => fail2!("bad item")
12881282
}
@@ -1628,7 +1622,7 @@ impl<'self> Visitor<()> for ImplVisitor<'self> {
16281622

16291623
// Load eagerly if this is an implementation of the Drop trait
16301624
// or if the trait is not defined in this crate.
1631-
if def_id == self.ecx.tcx.lang_items.drop_trait().unwrap() ||
1625+
if Some(def_id) == self.ecx.tcx.lang_items.drop_trait() ||
16321626
def_id.crate != LOCAL_CRATE {
16331627
self.ebml_w.start_tag(tag_impls_impl);
16341628
encode_def_id(self.ebml_w, local_def(item.id));
@@ -1744,7 +1738,6 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] {
17441738
diag,
17451739
tcx,
17461740
reexports2,
1747-
exported_items,
17481741
discrim_symbols,
17491742
cstore,
17501743
encode_inlined_item,
@@ -1760,7 +1753,6 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] {
17601753
tcx: tcx,
17611754
stats: stats,
17621755
reexports2: reexports2,
1763-
exported_items: exported_items,
17641756
item_symbols: item_symbols,
17651757
discrim_symbols: discrim_symbols,
17661758
non_inlineable_statics: non_inlineable_statics,

src/librustc/middle/borrowck/gather_loans/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ use syntax::ast_util::id_range;
3131
use syntax::codemap::Span;
3232
use syntax::print::pprust;
3333
use syntax::visit;
34-
use syntax::visit::Visitor;
35-
use syntax::ast::{Expr, fn_kind, fn_decl, Block, NodeId, Stmt, Pat, Local};
34+
use syntax::visit::{Visitor, fn_kind};
35+
use syntax::ast::{Expr, fn_decl, Block, NodeId, Stmt, Pat, Local};
3636

3737
mod lifetime;
3838
mod restrictions;

0 commit comments

Comments
 (0)