Skip to content

Commit daf28a4

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. Due to #4082, this only applies to enums that are in the same crate. r=pcwalton Closes #818
1 parent f89d4ac commit daf28a4

File tree

13 files changed

+186
-78
lines changed

13 files changed

+186
-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;
@@ -388,7 +387,7 @@ type ctxt =
388387
needs_unwind_cleanup_cache: HashMap<t, bool>,
389388
kind_cache: HashMap<t, Kind>,
390389
ast_ty_to_ty_cache: HashMap<@ast::Ty, ast_ty_to_ty_cache_entry>,
391-
enum_var_cache: HashMap<def_id, @~[variant_info]>,
390+
enum_var_cache: HashMap<def_id, @~[VariantInfo]>,
392391
trait_method_cache: HashMap<def_id, @~[method]>,
393392
ty_param_bounds: HashMap<ast::node_id, param_bounds>,
394393
inferred_modes: HashMap<ast::node_id, ast::mode>,
@@ -3638,19 +3637,28 @@ fn struct_ctor_id(cx: ctxt, struct_did: ast::def_id) -> Option<ast::def_id> {
36383637
}
36393638

36403639
// Enum information
3641-
type variant_info = @{args: ~[t], ctor_ty: t, name: ast::ident,
3642-
id: ast::def_id, disr_val: int};
3640+
struct VariantInfo_ {
3641+
args: ~[t],
3642+
ctor_ty: t,
3643+
name: ast::ident,
3644+
id: ast::def_id,
3645+
disr_val: int,
3646+
vis: visibility
3647+
}
3648+
3649+
type VariantInfo = @VariantInfo_;
36433650

36443651
fn substd_enum_variants(cx: ctxt,
36453652
id: ast::def_id,
3646-
substs: &substs) -> ~[variant_info] {
3653+
substs: &substs) -> ~[VariantInfo] {
36473654
do vec::map(*enum_variants(cx, id)) |variant_info| {
36483655
let substd_args = vec::map(variant_info.args,
36493656
|aty| subst(cx, substs, *aty));
36503657

36513658
let substd_ctor_ty = subst(cx, substs, variant_info.ctor_ty);
36523659

3653-
@{args: substd_args, ctor_ty: substd_ctor_ty, ..**variant_info}
3660+
@VariantInfo_{args: substd_args, ctor_ty: substd_ctor_ty,
3661+
..**variant_info}
36543662
}
36553663
}
36563664

@@ -3761,7 +3769,7 @@ fn item_path(cx: ctxt, id: ast::def_id) -> ast_map::path {
37613769
}
37623770

37633771
fn enum_is_univariant(cx: ctxt, id: ast::def_id) -> bool {
3764-
vec::len(*enum_variants(cx, id)) == 1u
3772+
enum_variants(cx, id).len() == 1
37653773
}
37663774

37673775
fn type_is_empty(cx: ctxt, t: t) -> bool {
@@ -3771,7 +3779,7 @@ fn type_is_empty(cx: ctxt, t: t) -> bool {
37713779
}
37723780
}
37733781

3774-
fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[variant_info] {
3782+
fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[VariantInfo] {
37753783
match cx.enum_var_cache.find(id) {
37763784
Some(variants) => return variants,
37773785
_ => { /* fallthrough */ }
@@ -3811,11 +3819,12 @@ fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[variant_info] {
38113819
}
38123820
_ => disr_val += 1
38133821
}
3814-
@{args: arg_tys,
3822+
@VariantInfo_{args: arg_tys,
38153823
ctor_ty: ctor_ty,
38163824
name: variant.node.name,
38173825
id: ast_util::local_def(variant.node.id),
3818-
disr_val: disr_val
3826+
disr_val: disr_val,
3827+
vis: variant.node.vis
38193828
}
38203829
}
38213830
ast::struct_variant_kind(_) => {
@@ -3837,13 +3846,13 @@ fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[variant_info] {
38373846

38383847
// Returns information about the enum variant with the given ID:
38393848
fn enum_variant_with_id(cx: ctxt, enum_id: ast::def_id,
3840-
variant_id: ast::def_id) -> variant_info {
3849+
variant_id: ast::def_id) -> VariantInfo {
38413850
let variants = enum_variants(cx, enum_id);
3842-
let mut i = 0u;
3843-
while i < vec::len::<variant_info>(*variants) {
3851+
let mut i = 0;
3852+
while i < variants.len() {
38443853
let variant = variants[i];
38453854
if variant.id == variant_id { return variant; }
3846-
i += 1u;
3855+
i += 1;
38473856
}
38483857
cx.sess.bug(~"enum_variant_with_id(): no variant exists with that ID");
38493858
}

0 commit comments

Comments
 (0)