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
This commit is contained in:
Tim Chevalier 2012-11-30 11:24:16 -08:00
parent f89d4ac830
commit daf28a421a
13 changed files with 186 additions and 78 deletions

View File

@ -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)

View File

@ -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| {
@ -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;

View File

@ -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};
@ -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;
}
@ -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);
@ -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 */ }
}
}
_ => {}
}

View File

@ -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;
@ -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,
@ -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();
@ -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 {
@ -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_) => {
@ -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);
}
}
@ -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| {

View File

@ -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");
@ -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;

View File

@ -1,4 +1,3 @@
// #[warn(deprecated_mode)];
#[warn(deprecated_pattern)];
use std::{map, smallintmap};
@ -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;
@ -388,7 +387,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>,
@ -3638,19 +3637,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}
}
}
@ -3761,7 +3769,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 {
@ -3771,7 +3779,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 */ }
@ -3811,11 +3819,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(_) => {
@ -3837,13 +3846,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");
}

View File

@ -68,7 +68,7 @@ type parameter).
use astconv::{ast_conv, ast_path_to_ty, ast_ty_to_ty};
use astconv::{ast_region_to_region};
use middle::ty::{TyVid, vid, FnTyBase, FnMeta, FnSig};
use middle::ty::{TyVid, vid, FnTyBase, FnMeta, FnSig, VariantInfo_};
use regionmanip::{replace_bound_regions_in_fn_ty};
use rscope::{anon_rscope, binding_rscope, empty_rscope, in_anon_rscope};
use rscope::{in_binding_rscope, region_scope, type_rscope,
@ -78,6 +78,7 @@ use typeck::infer::{resolve_type, force_tvar};
use result::{Result, Ok, Err};
use syntax::print::pprust;
use syntax::parse::token::special_idents;
use syntax::ast_util::{is_local, visibility_to_privacy, Private, Public};
use vtable::{LocationInfo, VtableContext};
use std::map::HashMap;
@ -1785,9 +1786,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
ast::deref => {
let sty = structure_of(fcx, expr.span, oprnd_t);
// deref'ing an unsafe pointer requires that we be in an unsafe
// context
match sty {
// deref'ing an unsafe pointer requires that we be in an unsafe
// context
ty::ty_ptr(*) => {
fcx.require_unsafe(
expr.span,
@ -1796,8 +1797,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
_ => { /*ok*/ }
}
match ty::deref_sty(tcx, &sty, true) {
Some(mt) => { oprnd_t = mt.ty }
let operand_ty = ty::deref_sty(tcx, &sty, true);
match operand_ty {
Some(mt) => {
oprnd_t = mt.ty
}
None => {
match sty {
ty::ty_enum(*) => {
@ -2460,7 +2465,7 @@ fn check_enum_variants(ccx: @crate_ctxt,
id: ast::node_id) {
fn do_check(ccx: @crate_ctxt, sp: span, vs: ~[ast::variant],
id: ast::node_id, disr_vals: &mut ~[int], disr_val: &mut int,
variants: &mut ~[ty::variant_info]) {
variants: &mut ~[ty::VariantInfo]) {
let rty = ty::node_id_to_type(ccx.tcx, id);
for vs.each |v| {
do v.node.disr_expr.iter |e_ref| {
@ -2522,9 +2527,9 @@ fn check_enum_variants(ccx: @crate_ctxt,
None => {}
Some(arg_tys) => {
variants.push(
@{args: arg_tys, ctor_ty: ctor_ty,
@VariantInfo_{args: arg_tys, ctor_ty: ctor_ty,
name: v.node.name, id: local_def(v.node.id),
disr_val: this_disr_val});
disr_val: this_disr_val, vis: v.node.vis});
}
}
}

View File

@ -45,7 +45,8 @@ use syntax::{ast, ast_util, ast_map};
use ast::spanned;
use ast::{required, provided};
use syntax::ast_map::node_id_to_str;
use syntax::ast_util::{local_def, respan, split_trait_methods};
use syntax::ast_util::{local_def, respan, split_trait_methods,
has_legacy_export_attr};
use syntax::visit;
use metadata::csearch;
use util::common::{block_query, loop_query};
@ -372,7 +373,8 @@ fn check_crate(tcx: ty::ctxt,
method_map: std::map::HashMap(),
vtable_map: std::map::HashMap(),
coherence_info: @coherence::CoherenceInfo(),
tcx: tcx});
tcx: tcx
});
collect::collect_item_types(ccx, crate);
coherence::check_coherence(ccx, crate);
deriving::check_deriving(ccx, crate);

View File

@ -391,6 +391,16 @@ fn node_id_to_str(map: map, id: node_id, itr: @ident_interner) -> ~str {
}
}
}
fn node_item_query<Result>(items: map, id: node_id,
query: fn(@item) -> Result,
error_msg: ~str) -> Result {
match items.find(id) {
Some(node_item(it, _)) => query(it),
_ => fail(error_msg)
}
}
// Local Variables:
// mode: rust
// fill-column: 78;

View File

@ -602,6 +602,46 @@ fn struct_def_is_tuple_like(struct_def: @ast::struct_def) -> bool {
struct_def.ctor_id.is_some()
}
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
}
}
}
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) }
}
fn has_legacy_export_attr(attrs: &[attribute]) -> bool {
for attrs.each |attribute| {
match attribute.node.value.node {
meta_word(w) if w == ~"legacy_exports" => {
return true;
}
_ => {}
}
}
return false;
}
// Local Variables:
// mode: rust
// fill-column: 78;

View File

@ -0,0 +1,5 @@
mod super_sekrit {
pub enum sooper_sekrit {
pub quux, priv baz
}
}

View File

@ -0,0 +1,14 @@
mod ctr {
pub enum ctr { priv mkCtr(int) }
pub fn new(i: int) -> ctr { mkCtr(i) }
pub fn inc(c: ctr) -> ctr { mkCtr(*c + 1) }
}
fn main() {
let c = ctr::new(42);
let c2 = ctr::inc(c);
assert *c2 == 5; //~ ERROR can only dereference enums with a single, public variant
}

View File

@ -0,0 +1,7 @@
// xfail-test
// aux-build:private_variant_1.rs
extern mod private_variant_1;
fn main() {
let _x = private_variant_1::super_sekrit::baz; //~ ERROR baz is private
}