Skip to content

Commit e17d39b

Browse files
committed
Disallow dereferencing enum types when the variant is private
If an enum type's only variant is private, disallow dereferencing values of its type, as per rust-lang#818. Due to rust-lang#4082, this only applies to enums that are in the same crate.
1 parent 946427b commit e17d39b

File tree

14 files changed

+187
-78
lines changed

14 files changed

+187
-78
lines changed

src/librustc/metadata/csearch.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ fn maybe_get_item_ast(tcx: ty::ctxt, def: ast::def_id,
9999
}
100100

101101
fn get_enum_variants(tcx: ty::ctxt, def: ast::def_id)
102-
-> ~[ty::variant_info] {
102+
-> ~[ty::VariantInfo] {
103103
let cstore = tcx.cstore;
104104
let cdata = cstore::get_crate_data(cstore, def.crate);
105105
return decoder::get_enum_variants(cstore.intr, cdata, def.node, tcx)

src/librustc/metadata/decoder.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -612,11 +612,11 @@ fn maybe_get_item_ast(intr: @ident_interner, cdata: cmd, tcx: ty::ctxt,
612612
}
613613

614614
fn get_enum_variants(intr: @ident_interner, cdata: cmd, id: ast::node_id,
615-
tcx: ty::ctxt) -> ~[ty::variant_info] {
615+
tcx: ty::ctxt) -> ~[ty::VariantInfo] {
616616
let data = cdata.data;
617617
let items = Reader::get_doc(Reader::Doc(data), tag_items);
618618
let item = find_item(id, items);
619-
let mut infos: ~[ty::variant_info] = ~[];
619+
let mut infos: ~[ty::VariantInfo] = ~[];
620620
let variant_ids = enum_variant_ids(item, cdata);
621621
let mut disr_val = 0;
622622
for variant_ids.each |did| {
@@ -634,8 +634,11 @@ fn get_enum_variants(intr: @ident_interner, cdata: cmd, id: ast::node_id,
634634
Some(val) => { disr_val = val; }
635635
_ => { /* empty */ }
636636
}
637-
infos.push(@{args: arg_tys, ctor_ty: ctor_ty, name: name,
638-
id: *did, disr_val: disr_val});
637+
infos.push(@ty::VariantInfo_{args: arg_tys,
638+
ctor_ty: ctor_ty, name: name,
639+
// I'm not even sure if we encode visibility
640+
// for variants -- TEST -- tjc
641+
id: *did, disr_val: disr_val, vis: ast::inherited});
639642
disr_val += 1;
640643
}
641644
return infos;

src/librustc/middle/privacy.rs

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
33

44
use /*mod*/ syntax::ast;
55
use /*mod*/ syntax::visit;
6-
use syntax::ast::{def_variant, expr_field, expr_struct, ident, item_class};
7-
use syntax::ast::{item_impl, item_trait, local_crate, node_id, pat_struct};
6+
use syntax::ast_map;
7+
use syntax::ast::{def_variant, expr_field, expr_struct, expr_unary, ident,
8+
item_class};
9+
use syntax::ast::{item_impl, item_trait, item_enum, local_crate, node_id,
10+
pat_struct};
811
use syntax::ast::{private, provided, required};
912
use syntax::ast_map::{node_item, node_method};
13+
use syntax::ast_util::{has_legacy_export_attr, is_local,
14+
visibility_to_privacy, Private, Public};
1015
use ty::{ty_class, ty_enum};
1116
use typeck::{method_map, method_origin, method_param, method_self};
1217
use typeck::{method_static, method_trait};
@@ -16,13 +21,15 @@ use dvec::DVec;
1621

1722
fn check_crate(tcx: ty::ctxt, method_map: &method_map, crate: @ast::crate) {
1823
let privileged_items = @DVec();
24+
let legacy_exports = has_legacy_export_attr(crate.node.attrs);
1925

2026
// Adds structs that are privileged to this scope.
2127
let add_privileged_items = |items: &[@ast::item]| {
2228
let mut count = 0;
2329
for items.each |item| {
2430
match item.node {
25-
item_class(*) | item_trait(*) | item_impl(*) => {
31+
item_class(*) | item_trait(*) | item_impl(*)
32+
| item_enum(*) => {
2633
privileged_items.push(item.id);
2734
count += 1;
2835
}
@@ -32,6 +39,34 @@ fn check_crate(tcx: ty::ctxt, method_map: &method_map, crate: @ast::crate) {
3239
count
3340
};
3441

42+
// Checks that an enum variant is in scope
43+
let check_variant = |span, enum_id| {
44+
let variant_info = ty::enum_variants(tcx, enum_id)[0];
45+
let parental_privacy = if is_local(enum_id) {
46+
let parent_vis = ast_map::node_item_query(tcx.items, enum_id.node,
47+
|it| { it.vis },
48+
~"unbound enum parent when checking \
49+
dereference of enum type");
50+
visibility_to_privacy(parent_vis, legacy_exports)
51+
}
52+
else {
53+
// WRONG
54+
Public
55+
};
56+
debug!("parental_privacy = %?", parental_privacy);
57+
debug!("vis = %?, priv = %?, legacy_exports = %?",
58+
variant_info.vis,
59+
visibility_to_privacy(variant_info.vis, legacy_exports),
60+
legacy_exports);
61+
// inherited => privacy of the enum item
62+
if visibility_to_privacy(variant_info.vis,
63+
parental_privacy == Public) == Private {
64+
tcx.sess.span_err(span,
65+
~"can only dereference enums \
66+
with a single, public variant");
67+
}
68+
};
69+
3570
// Checks that a private field is in scope.
3671
let check_field = |span, id, ident| {
3772
let fields = ty::lookup_class_fields(tcx, id);
@@ -222,6 +257,21 @@ fn check_crate(tcx: ty::ctxt, method_map: &method_map, crate: @ast::crate) {
222257
}
223258
}
224259
}
260+
expr_unary(ast::deref, operand) => {
261+
// In *e, we need to check that if e's type is an
262+
// enum type t, then t's first variant is public or
263+
// privileged. (We can assume it has only one variant
264+
// since typeck already happened.)
265+
match ty::get(ty::expr_ty(tcx, operand)).sty {
266+
ty_enum(id, _) => {
267+
if id.crate != local_crate ||
268+
!privileged_items.contains(&(id.node)) {
269+
check_variant(expr.span, id);
270+
}
271+
}
272+
_ => { /* No check needed */ }
273+
}
274+
}
225275
_ => {}
226276
}
227277

src/librustc/middle/resolve.rs

Lines changed: 6 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ use syntax::ast::{view_item_use, view_path_glob, view_path_list};
4444
use syntax::ast::{view_path_simple, visibility, anonymous, named};
4545
use syntax::ast_util::{def_id_of_def, dummy_sp, local_def};
4646
use syntax::ast_util::{path_to_ident, walk_pat, trait_method_to_ty_method};
47+
use syntax::ast_util::{Privacy, Public, Private, visibility_to_privacy};
48+
use syntax::ast_util::has_legacy_export_attr;
4749
use syntax::attr::{attr_metas, contains_name};
4850
use syntax::print::pprust::{pat_to_str, path_to_str};
4951
use syntax::codemap::span;
@@ -539,18 +541,6 @@ fn unused_import_lint_level(session: Session) -> level {
539541
return allow;
540542
}
541543

542-
enum Privacy {
543-
Private,
544-
Public
545-
}
546-
547-
impl Privacy : cmp::Eq {
548-
pure fn eq(&self, other: &Privacy) -> bool {
549-
((*self) as uint) == ((*other) as uint)
550-
}
551-
pure fn ne(&self, other: &Privacy) -> bool { !(*self).eq(other) }
552-
}
553-
554544
// Records a possibly-private type definition.
555545
struct TypeNsDef {
556546
mut privacy: Privacy,
@@ -780,18 +770,6 @@ fn namespace_to_str(ns: Namespace) -> ~str {
780770
}
781771
}
782772

783-
fn has_legacy_export_attr(attrs: &[syntax::ast::attribute]) -> bool {
784-
for attrs.each |attribute| {
785-
match attribute.node.value.node {
786-
syntax::ast::meta_word(w) if w == ~"legacy_exports" => {
787-
return true;
788-
}
789-
_ => {}
790-
}
791-
}
792-
return false;
793-
}
794-
795773
fn Resolver(session: Session, lang_items: LanguageItems,
796774
crate: @crate) -> Resolver {
797775
let graph_root = @NameBindings();
@@ -950,21 +928,6 @@ impl Resolver {
950928
}));
951929
}
952930

953-
fn visibility_to_privacy(visibility: visibility,
954-
legacy_exports: bool) -> Privacy {
955-
if legacy_exports {
956-
match visibility {
957-
inherited | public => Public,
958-
private => Private
959-
}
960-
} else {
961-
match visibility {
962-
public => Public,
963-
inherited | private => Private
964-
}
965-
}
966-
}
967-
968931
/// Returns the current module tracked by the reduced graph parent.
969932
fn get_module_from_parent(reduced_graph_parent: ReducedGraphParent)
970933
-> @Module {
@@ -1121,7 +1084,7 @@ impl Resolver {
11211084
let legacy = match parent {
11221085
ModuleReducedGraphParent(m) => m.legacy_exports
11231086
};
1124-
let privacy = self.visibility_to_privacy(item.vis, legacy);
1087+
let privacy = visibility_to_privacy(item.vis, legacy);
11251088

11261089
match item.node {
11271090
item_mod(module_) => {
@@ -1205,8 +1168,8 @@ impl Resolver {
12051168
self.build_reduced_graph_for_variant(*variant,
12061169
local_def(item.id),
12071170
// inherited => privacy of the enum item
1208-
self.visibility_to_privacy(variant.node.vis,
1209-
privacy == Public),
1171+
visibility_to_privacy(variant.node.vis,
1172+
privacy == Public),
12101173
new_parent, visitor);
12111174
}
12121175
}
@@ -1455,7 +1418,7 @@ impl Resolver {
14551418
let legacy = match parent {
14561419
ModuleReducedGraphParent(m) => m.legacy_exports
14571420
};
1458-
let privacy = self.visibility_to_privacy(view_item.vis, legacy);
1421+
let privacy = visibility_to_privacy(view_item.vis, legacy);
14591422
match view_item.node {
14601423
view_item_import(view_paths) => {
14611424
for view_paths.each |view_path| {

src/librustc/middle/trans/base.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t,
517517
let _icx = cx.insn_ctxt("iter_structural_ty");
518518

519519
fn iter_variant(cx: block, a_tup: ValueRef,
520-
variant: ty::variant_info,
520+
variant: ty::VariantInfo,
521521
tps: ~[ty::t], tid: ast::def_id,
522522
f: val_and_ty_fn) -> block {
523523
let _icx = cx.insn_ctxt("iter_variant");
@@ -1802,7 +1802,7 @@ fn trans_class_dtor(ccx: @crate_ctxt, path: path,
18021802

18031803
fn trans_enum_def(ccx: @crate_ctxt, enum_definition: ast::enum_def,
18041804
id: ast::node_id, tps: ~[ast::ty_param], degen: bool,
1805-
path: @ast_map::path, vi: @~[ty::variant_info],
1805+
path: @ast_map::path, vi: @~[ty::VariantInfo],
18061806
i: &mut uint) {
18071807
for vec::each(enum_definition.variants) |variant| {
18081808
let disr_val = vi[*i].disr_val;

src/librustc/middle/ty.rs

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// #[warn(deprecated_mode)];
21
#[warn(deprecated_pattern)];
32

43
use std::{map, smallintmap};
@@ -158,7 +157,7 @@ export resolved_mode;
158157
export arg_mode;
159158
export unify_mode;
160159
export set_default_mode;
161-
export variant_info;
160+
export VariantInfo, VariantInfo_;
162161
export walk_ty, maybe_walk_ty;
163162
export occurs_check;
164163
export param_ty;
@@ -404,7 +403,7 @@ type ctxt =
404403
needs_unwind_cleanup_cache: HashMap<t, bool>,
405404
kind_cache: HashMap<t, Kind>,
406405
ast_ty_to_ty_cache: HashMap<@ast::Ty, ast_ty_to_ty_cache_entry>,
407-
enum_var_cache: HashMap<def_id, @~[variant_info]>,
406+
enum_var_cache: HashMap<def_id, @~[VariantInfo]>,
408407
trait_method_cache: HashMap<def_id, @~[method]>,
409408
ty_param_bounds: HashMap<ast::node_id, param_bounds>,
410409
inferred_modes: HashMap<ast::node_id, ast::mode>,
@@ -3938,19 +3937,28 @@ fn struct_ctor_id(cx: ctxt, struct_did: ast::def_id) -> Option<ast::def_id> {
39383937
}
39393938

39403939
// Enum information
3941-
type variant_info = @{args: ~[t], ctor_ty: t, name: ast::ident,
3942-
id: ast::def_id, disr_val: int};
3940+
struct VariantInfo_ {
3941+
args: ~[t],
3942+
ctor_ty: t,
3943+
name: ast::ident,
3944+
id: ast::def_id,
3945+
disr_val: int,
3946+
vis: visibility
3947+
}
3948+
3949+
type VariantInfo = @VariantInfo_;
39433950

39443951
fn substd_enum_variants(cx: ctxt,
39453952
id: ast::def_id,
3946-
substs: &substs) -> ~[variant_info] {
3953+
substs: &substs) -> ~[VariantInfo] {
39473954
do vec::map(*enum_variants(cx, id)) |variant_info| {
39483955
let substd_args = vec::map(variant_info.args,
39493956
|aty| subst(cx, substs, *aty));
39503957

39513958
let substd_ctor_ty = subst(cx, substs, variant_info.ctor_ty);
39523959

3953-
@{args: substd_args, ctor_ty: substd_ctor_ty, ..**variant_info}
3960+
@VariantInfo_{args: substd_args, ctor_ty: substd_ctor_ty,
3961+
..**variant_info}
39543962
}
39553963
}
39563964

@@ -4061,7 +4069,7 @@ fn item_path(cx: ctxt, id: ast::def_id) -> ast_map::path {
40614069
}
40624070

40634071
fn enum_is_univariant(cx: ctxt, id: ast::def_id) -> bool {
4064-
vec::len(*enum_variants(cx, id)) == 1u
4072+
enum_variants(cx, id).len() == 1
40654073
}
40664074

40674075
fn type_is_empty(cx: ctxt, t: t) -> bool {
@@ -4071,7 +4079,7 @@ fn type_is_empty(cx: ctxt, t: t) -> bool {
40714079
}
40724080
}
40734081

4074-
fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[variant_info] {
4082+
fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[VariantInfo] {
40754083
match cx.enum_var_cache.find(id) {
40764084
Some(variants) => return variants,
40774085
_ => { /* fallthrough */ }
@@ -4111,11 +4119,12 @@ fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[variant_info] {
41114119
}
41124120
_ => disr_val += 1
41134121
}
4114-
@{args: arg_tys,
4122+
@VariantInfo_{args: arg_tys,
41154123
ctor_ty: ctor_ty,
41164124
name: variant.node.name,
41174125
id: ast_util::local_def(variant.node.id),
4118-
disr_val: disr_val
4126+
disr_val: disr_val,
4127+
vis: variant.node.vis
41194128
}
41204129
}
41214130
ast::struct_variant_kind(_) => {
@@ -4137,13 +4146,13 @@ fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[variant_info] {
41374146

41384147
// Returns information about the enum variant with the given ID:
41394148
fn enum_variant_with_id(cx: ctxt, enum_id: ast::def_id,
4140-
variant_id: ast::def_id) -> variant_info {
4149+
variant_id: ast::def_id) -> VariantInfo {
41414150
let variants = enum_variants(cx, enum_id);
4142-
let mut i = 0u;
4143-
while i < vec::len::<variant_info>(*variants) {
4151+
let mut i = 0;
4152+
while i < variants.len() {
41444153
let variant = variants[i];
41454154
if variant.id == variant_id { return variant; }
4146-
i += 1u;
4155+
i += 1;
41474156
}
41484157
cx.sess.bug(~"enum_variant_with_id(): no variant exists with that ID");
41494158
}

0 commit comments

Comments
 (0)