Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disallow dereferencing enum types when the variant is private #4084

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/librustc/metadata/csearch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ fn maybe_get_item_ast(tcx: ty::ctxt, def: ast::def_id,
}

fn get_enum_variants(tcx: ty::ctxt, def: ast::def_id)
-> ~[ty::variant_info] {
-> ~[ty::VariantInfo] {
let cstore = tcx.cstore;
let cdata = cstore::get_crate_data(cstore, def.crate);
return decoder::get_enum_variants(cstore.intr, cdata, def.node, tcx)
Expand Down
11 changes: 7 additions & 4 deletions src/librustc/metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,11 +612,11 @@ fn maybe_get_item_ast(intr: @ident_interner, cdata: cmd, tcx: ty::ctxt,
}

fn get_enum_variants(intr: @ident_interner, cdata: cmd, id: ast::node_id,
tcx: ty::ctxt) -> ~[ty::variant_info] {
tcx: ty::ctxt) -> ~[ty::VariantInfo] {
let data = cdata.data;
let items = Reader::get_doc(Reader::Doc(data), tag_items);
let item = find_item(id, items);
let mut infos: ~[ty::variant_info] = ~[];
let mut infos: ~[ty::VariantInfo] = ~[];
let variant_ids = enum_variant_ids(item, cdata);
let mut disr_val = 0;
for variant_ids.each |did| {
Expand All @@ -634,8 +634,11 @@ fn get_enum_variants(intr: @ident_interner, cdata: cmd, id: ast::node_id,
Some(val) => { disr_val = val; }
_ => { /* empty */ }
}
infos.push(@{args: arg_tys, ctor_ty: ctor_ty, name: name,
id: *did, disr_val: disr_val});
infos.push(@ty::VariantInfo_{args: arg_tys,
ctor_ty: ctor_ty, name: name,
// I'm not even sure if we encode visibility
// for variants -- TEST -- tjc
id: *did, disr_val: disr_val, vis: ast::inherited});
disr_val += 1;
}
return infos;
Expand Down
56 changes: 53 additions & 3 deletions src/librustc/middle/privacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@

use /*mod*/ syntax::ast;
use /*mod*/ syntax::visit;
use syntax::ast::{def_variant, expr_field, expr_struct, ident, item_class};
use syntax::ast::{item_impl, item_trait, local_crate, node_id, pat_struct};
use syntax::ast_map;
use syntax::ast::{def_variant, expr_field, expr_struct, expr_unary, ident,
item_class};
use syntax::ast::{item_impl, item_trait, item_enum, local_crate, node_id,
pat_struct};
use syntax::ast::{private, provided, required};
use syntax::ast_map::{node_item, node_method};
use syntax::ast_util::{has_legacy_export_attr, is_local,
visibility_to_privacy, Private, Public};
use ty::{ty_class, ty_enum};
use typeck::{method_map, method_origin, method_param, method_self};
use typeck::{method_static, method_trait};
Expand All @@ -16,13 +21,15 @@ use dvec::DVec;

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

// Adds structs that are privileged to this scope.
let add_privileged_items = |items: &[@ast::item]| {
let mut count = 0;
for items.each |item| {
match item.node {
item_class(*) | item_trait(*) | item_impl(*) => {
item_class(*) | item_trait(*) | item_impl(*)
| item_enum(*) => {
privileged_items.push(item.id);
count += 1;
}
Expand All @@ -32,6 +39,34 @@ fn check_crate(tcx: ty::ctxt, method_map: &method_map, crate: @ast::crate) {
count
};

// Checks that an enum variant is in scope
let check_variant = |span, enum_id| {
let variant_info = ty::enum_variants(tcx, enum_id)[0];
let parental_privacy = if is_local(enum_id) {
let parent_vis = ast_map::node_item_query(tcx.items, enum_id.node,
|it| { it.vis },
~"unbound enum parent when checking \
dereference of enum type");
visibility_to_privacy(parent_vis, legacy_exports)
}
else {
// WRONG
Public
};
debug!("parental_privacy = %?", parental_privacy);
debug!("vis = %?, priv = %?, legacy_exports = %?",
variant_info.vis,
visibility_to_privacy(variant_info.vis, legacy_exports),
legacy_exports);
// inherited => privacy of the enum item
if visibility_to_privacy(variant_info.vis,
parental_privacy == Public) == Private {
tcx.sess.span_err(span,
~"can only dereference enums \
with a single, public variant");
}
};

// Checks that a private field is in scope.
let check_field = |span, id, ident| {
let fields = ty::lookup_class_fields(tcx, id);
Expand Down Expand Up @@ -222,6 +257,21 @@ fn check_crate(tcx: ty::ctxt, method_map: &method_map, crate: @ast::crate) {
}
}
}
expr_unary(ast::deref, operand) => {
// In *e, we need to check that if e's type is an
// enum type t, then t's first variant is public or
// privileged. (We can assume it has only one variant
// since typeck already happened.)
match ty::get(ty::expr_ty(tcx, operand)).sty {
ty_enum(id, _) => {
if id.crate != local_crate ||
!privileged_items.contains(&(id.node)) {
check_variant(expr.span, id);
}
}
_ => { /* No check needed */ }
}
}
_ => {}
}

Expand Down
49 changes: 6 additions & 43 deletions src/librustc/middle/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ use syntax::ast::{view_item_use, view_path_glob, view_path_list};
use syntax::ast::{view_path_simple, visibility, anonymous, named};
use syntax::ast_util::{def_id_of_def, dummy_sp, local_def};
use syntax::ast_util::{path_to_ident, walk_pat, trait_method_to_ty_method};
use syntax::ast_util::{Privacy, Public, Private, visibility_to_privacy};
use syntax::ast_util::has_legacy_export_attr;
use syntax::attr::{attr_metas, contains_name};
use syntax::print::pprust::{pat_to_str, path_to_str};
use syntax::codemap::span;
Expand Down Expand Up @@ -539,18 +541,6 @@ fn unused_import_lint_level(session: Session) -> level {
return allow;
}

enum Privacy {
Private,
Public
}

impl Privacy : cmp::Eq {
pure fn eq(&self, other: &Privacy) -> bool {
((*self) as uint) == ((*other) as uint)
}
pure fn ne(&self, other: &Privacy) -> bool { !(*self).eq(other) }
}

// Records a possibly-private type definition.
struct TypeNsDef {
mut privacy: Privacy,
Expand Down Expand Up @@ -780,18 +770,6 @@ fn namespace_to_str(ns: Namespace) -> ~str {
}
}

fn has_legacy_export_attr(attrs: &[syntax::ast::attribute]) -> bool {
for attrs.each |attribute| {
match attribute.node.value.node {
syntax::ast::meta_word(w) if w == ~"legacy_exports" => {
return true;
}
_ => {}
}
}
return false;
}

fn Resolver(session: Session, lang_items: LanguageItems,
crate: @crate) -> Resolver {
let graph_root = @NameBindings();
Expand Down Expand Up @@ -950,21 +928,6 @@ impl Resolver {
}));
}

fn visibility_to_privacy(visibility: visibility,
legacy_exports: bool) -> Privacy {
if legacy_exports {
match visibility {
inherited | public => Public,
private => Private
}
} else {
match visibility {
public => Public,
inherited | private => Private
}
}
}

/// Returns the current module tracked by the reduced graph parent.
fn get_module_from_parent(reduced_graph_parent: ReducedGraphParent)
-> @Module {
Expand Down Expand Up @@ -1121,7 +1084,7 @@ impl Resolver {
let legacy = match parent {
ModuleReducedGraphParent(m) => m.legacy_exports
};
let privacy = self.visibility_to_privacy(item.vis, legacy);
let privacy = visibility_to_privacy(item.vis, legacy);

match item.node {
item_mod(module_) => {
Expand Down Expand Up @@ -1205,8 +1168,8 @@ impl Resolver {
self.build_reduced_graph_for_variant(*variant,
local_def(item.id),
// inherited => privacy of the enum item
self.visibility_to_privacy(variant.node.vis,
privacy == Public),
visibility_to_privacy(variant.node.vis,
privacy == Public),
new_parent, visitor);
}
}
Expand Down Expand Up @@ -1455,7 +1418,7 @@ impl Resolver {
let legacy = match parent {
ModuleReducedGraphParent(m) => m.legacy_exports
};
let privacy = self.visibility_to_privacy(view_item.vis, legacy);
let privacy = visibility_to_privacy(view_item.vis, legacy);
match view_item.node {
view_item_import(view_paths) => {
for view_paths.each |view_path| {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t,
let _icx = cx.insn_ctxt("iter_structural_ty");

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

fn trans_enum_def(ccx: @crate_ctxt, enum_definition: ast::enum_def,
id: ast::node_id, tps: ~[ast::ty_param], degen: bool,
path: @ast_map::path, vi: @~[ty::variant_info],
path: @ast_map::path, vi: @~[ty::VariantInfo],
i: &mut uint) {
for vec::each(enum_definition.variants) |variant| {
let disr_val = vi[*i].disr_val;
Expand Down
39 changes: 24 additions & 15 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// #[warn(deprecated_mode)];
#[warn(deprecated_pattern)];

use std::{map, smallintmap};
Expand Down Expand Up @@ -158,7 +157,7 @@ export resolved_mode;
export arg_mode;
export unify_mode;
export set_default_mode;
export variant_info;
export VariantInfo, VariantInfo_;
export walk_ty, maybe_walk_ty;
export occurs_check;
export param_ty;
Expand Down Expand Up @@ -404,7 +403,7 @@ type ctxt =
needs_unwind_cleanup_cache: HashMap<t, bool>,
kind_cache: HashMap<t, Kind>,
ast_ty_to_ty_cache: HashMap<@ast::Ty, ast_ty_to_ty_cache_entry>,
enum_var_cache: HashMap<def_id, @~[variant_info]>,
enum_var_cache: HashMap<def_id, @~[VariantInfo]>,
trait_method_cache: HashMap<def_id, @~[method]>,
ty_param_bounds: HashMap<ast::node_id, param_bounds>,
inferred_modes: HashMap<ast::node_id, ast::mode>,
Expand Down Expand Up @@ -3938,19 +3937,28 @@ fn struct_ctor_id(cx: ctxt, struct_did: ast::def_id) -> Option<ast::def_id> {
}

// Enum information
type variant_info = @{args: ~[t], ctor_ty: t, name: ast::ident,
id: ast::def_id, disr_val: int};
struct VariantInfo_ {
args: ~[t],
ctor_ty: t,
name: ast::ident,
id: ast::def_id,
disr_val: int,
vis: visibility
}

type VariantInfo = @VariantInfo_;

fn substd_enum_variants(cx: ctxt,
id: ast::def_id,
substs: &substs) -> ~[variant_info] {
substs: &substs) -> ~[VariantInfo] {
do vec::map(*enum_variants(cx, id)) |variant_info| {
let substd_args = vec::map(variant_info.args,
|aty| subst(cx, substs, *aty));

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

@{args: substd_args, ctor_ty: substd_ctor_ty, ..**variant_info}
@VariantInfo_{args: substd_args, ctor_ty: substd_ctor_ty,
..**variant_info}
}
}

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

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

fn type_is_empty(cx: ctxt, t: t) -> bool {
Expand All @@ -4071,7 +4079,7 @@ fn type_is_empty(cx: ctxt, t: t) -> bool {
}
}

fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[variant_info] {
fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[VariantInfo] {
match cx.enum_var_cache.find(id) {
Some(variants) => return variants,
_ => { /* fallthrough */ }
Expand Down Expand Up @@ -4111,11 +4119,12 @@ fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[variant_info] {
}
_ => disr_val += 1
}
@{args: arg_tys,
@VariantInfo_{args: arg_tys,
ctor_ty: ctor_ty,
name: variant.node.name,
id: ast_util::local_def(variant.node.id),
disr_val: disr_val
disr_val: disr_val,
vis: variant.node.vis
}
}
ast::struct_variant_kind(_) => {
Expand All @@ -4137,13 +4146,13 @@ fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[variant_info] {

// Returns information about the enum variant with the given ID:
fn enum_variant_with_id(cx: ctxt, enum_id: ast::def_id,
variant_id: ast::def_id) -> variant_info {
variant_id: ast::def_id) -> VariantInfo {
let variants = enum_variants(cx, enum_id);
let mut i = 0u;
while i < vec::len::<variant_info>(*variants) {
let mut i = 0;
while i < variants.len() {
let variant = variants[i];
if variant.id == variant_id { return variant; }
i += 1u;
i += 1;
}
cx.sess.bug(~"enum_variant_with_id(): no variant exists with that ID");
}
Expand Down
Loading