Skip to content

Commit 6831979

Browse files
committed
rustc: Switch tuple structs to have private fields
This is a continuation of the work done in #13184 to make struct fields private by default. This commit finishes RFC 4 by making all tuple structs have private fields by default. Note that enum variants are not affected. A tuple struct having a private field means that it cannot be matched on in a pattern match (both refutable and irrefutable), and it also cannot have a value specified to be constructed. Similarly to private fields, switching the type of a private field in a tuple struct should be able to be done in a backwards compatible way. The one snag that I ran into which wasn't mentioned in the RFC is that this commit also forbids taking the value of a tuple struct constructor. For example, this code now fails to compile: mod a { pub struct A(int); } let a: fn(int) -> a::A = a::A; //~ ERROR: first field is private Although no fields are bound in this example, it exposes implementation details through the type itself. For this reason, taking the value of a struct constructor with private fields is forbidden (outside the containing module). RFC: 0004-private-fields
1 parent b8ef9fd commit 6831979

File tree

6 files changed

+270
-31
lines changed

6 files changed

+270
-31
lines changed

src/librustc/metadata/csearch.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,11 @@ pub fn get_exported_macros(cstore: &cstore::CStore,
311311
let cdata = cstore.get_crate_data(crate_num);
312312
decoder::get_exported_macros(cdata)
313313
}
314+
315+
pub fn get_tuple_struct_definition_if_ctor(cstore: &cstore::CStore,
316+
def_id: ast::DefId)
317+
-> Option<ast::DefId>
318+
{
319+
let cdata = cstore.get_crate_data(def_id.krate);
320+
decoder::get_tuple_struct_definition_if_ctor(cdata, def_id.node)
321+
}

src/librustc/metadata/decoder.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -962,23 +962,26 @@ pub fn get_static_methods_if_impl(intr: Rc<IdentInterner>,
962962
/// If node_id is the constructor of a tuple struct, retrieve the NodeId of
963963
/// the actual type definition, otherwise, return None
964964
pub fn get_tuple_struct_definition_if_ctor(cdata: Cmd,
965-
node_id: ast::NodeId) -> Option<ast::NodeId> {
965+
node_id: ast::NodeId)
966+
-> Option<ast::DefId>
967+
{
966968
let item = lookup_item(node_id, cdata.data());
967969
let mut ret = None;
968970
reader::tagged_docs(item, tag_items_data_item_is_tuple_struct_ctor, |_| {
969971
ret = Some(item_reqd_and_translated_parent_item(cdata.cnum, item));
970972
false
971973
});
972-
ret.map(|x| x.node)
974+
ret
973975
}
974976

975977
pub fn get_item_attrs(cdata: Cmd,
976-
node_id: ast::NodeId,
978+
orig_node_id: ast::NodeId,
977979
f: |Vec<@ast::MetaItem> |) {
978980
// The attributes for a tuple struct are attached to the definition, not the ctor;
979981
// we assume that someone passing in a tuple struct ctor is actually wanting to
980982
// look at the definition
981-
let node_id = get_tuple_struct_definition_if_ctor(cdata, node_id).unwrap_or(node_id);
983+
let node_id = get_tuple_struct_definition_if_ctor(cdata, orig_node_id);
984+
let node_id = node_id.map(|x| x.node).unwrap_or(orig_node_id);
982985
let item = lookup_item(node_id, cdata.data());
983986
reader::tagged_docs(item, tag_attributes, |attributes| {
984987
reader::tagged_docs(attributes, tag_attribute, |attribute| {

src/librustc/middle/privacy.rs

Lines changed: 98 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
1515
use std::mem::replace;
1616

17+
use metadata::csearch;
1718
use middle::lint;
1819
use middle::resolve;
1920
use middle::ty;
@@ -358,6 +359,12 @@ enum PrivacyResult {
358359
DisallowedBy(ast::NodeId),
359360
}
360361

362+
enum FieldName {
363+
UnnamedField(uint), // index
364+
// FIXME #6993: change type (and name) from Ident to Name
365+
NamedField(ast::Ident),
366+
}
367+
361368
impl<'a> PrivacyVisitor<'a> {
362369
// used when debugging
363370
fn nodestr(&self, id: ast::NodeId) -> ~str {
@@ -560,18 +567,23 @@ impl<'a> PrivacyVisitor<'a> {
560567
}
561568

562569
// Checks that a field is in scope.
563-
// FIXME #6993: change type (and name) from Ident to Name
564-
fn check_field(&mut self, span: Span, id: ast::DefId, ident: ast::Ident) {
565-
for field in ty::lookup_struct_fields(self.tcx, id).iter() {
566-
if field.name != ident.name { continue; }
567-
if field.vis == ast::Public { break }
568-
if !is_local(field.id) ||
569-
!self.private_accessible(field.id.node) {
570-
self.tcx.sess.span_err(span,
571-
format!("field `{}` is private",
572-
token::get_ident(ident)))
570+
fn check_field(&mut self, span: Span, id: ast::DefId,
571+
name: FieldName) {
572+
let fields = ty::lookup_struct_fields(self.tcx, id);
573+
let field = match name {
574+
NamedField(ident) => {
575+
fields.iter().find(|f| f.name == ident.name).unwrap()
573576
}
574-
break;
577+
UnnamedField(idx) => fields.get(idx)
578+
};
579+
if field.vis == ast::Public { return }
580+
if !is_local(field.id) || !self.private_accessible(field.id.node) {
581+
let msg = match name {
582+
NamedField(name) => format!("field `{}` is private",
583+
token::get_ident(name)),
584+
UnnamedField(idx) => format!("field \\#{} is private", idx + 1),
585+
};
586+
self.tcx.sess.span_err(span, msg);
575587
}
576588
}
577589

@@ -634,10 +646,11 @@ impl<'a> PrivacyVisitor<'a> {
634646
_ => {},
635647
}
636648
}
637-
// If an import is not used in either namespace, we still want to check
638-
// that it could be legal. Therefore we check in both namespaces and only
639-
// report an error if both would be illegal. We only report one error,
640-
// even if it is illegal to import from both namespaces.
649+
// If an import is not used in either namespace, we still
650+
// want to check that it could be legal. Therefore we check
651+
// in both namespaces and only report an error if both would
652+
// be illegal. We only report one error, even if it is
653+
// illegal to import from both namespaces.
641654
match (value_priv, check_value, type_priv, check_type) {
642655
(Some(p), resolve::Unused, None, _) |
643656
(None, _, Some(p), resolve::Unused) => {
@@ -701,7 +714,8 @@ impl<'a> PrivacyVisitor<'a> {
701714
// is whether the trait itself is accessible or not.
702715
MethodParam(MethodParam { trait_id: trait_id, .. }) |
703716
MethodObject(MethodObject { trait_id: trait_id, .. }) => {
704-
self.report_error(self.ensure_public(span, trait_id, None, "source trait"));
717+
self.report_error(self.ensure_public(span, trait_id, None,
718+
"source trait"));
705719
}
706720
}
707721
}
@@ -726,7 +740,7 @@ impl<'a> Visitor<()> for PrivacyVisitor<'a> {
726740
match ty::get(ty::expr_ty_adjusted(self.tcx, base,
727741
&*self.method_map.borrow())).sty {
728742
ty::ty_struct(id, _) => {
729-
self.check_field(expr.span, id, ident);
743+
self.check_field(expr.span, id, NamedField(ident));
730744
}
731745
_ => {}
732746
}
@@ -749,15 +763,16 @@ impl<'a> Visitor<()> for PrivacyVisitor<'a> {
749763
match ty::get(ty::expr_ty(self.tcx, expr)).sty {
750764
ty::ty_struct(id, _) => {
751765
for field in (*fields).iter() {
752-
self.check_field(expr.span, id, field.ident.node);
766+
self.check_field(expr.span, id,
767+
NamedField(field.ident.node));
753768
}
754769
}
755770
ty::ty_enum(_, _) => {
756771
match self.tcx.def_map.borrow().get_copy(&expr.id) {
757772
ast::DefVariant(_, variant_id, _) => {
758773
for field in fields.iter() {
759774
self.check_field(expr.span, variant_id,
760-
field.ident.node);
775+
NamedField(field.ident.node));
761776
}
762777
}
763778
_ => self.tcx.sess.span_bug(expr.span,
@@ -772,6 +787,46 @@ impl<'a> Visitor<()> for PrivacyVisitor<'a> {
772787
struct type?!"),
773788
}
774789
}
790+
ast::ExprPath(..) => {
791+
let guard = |did: ast::DefId| {
792+
let fields = ty::lookup_struct_fields(self.tcx, did);
793+
let any_priv = fields.iter().any(|f| {
794+
f.vis != ast::Public && (
795+
!is_local(f.id) ||
796+
!self.private_accessible(f.id.node))
797+
});
798+
if any_priv {
799+
self.tcx.sess.span_err(expr.span,
800+
"cannot invoke tuple struct constructor \
801+
with private fields");
802+
}
803+
};
804+
match self.tcx.def_map.borrow().find(&expr.id) {
805+
Some(&ast::DefStruct(did)) => {
806+
guard(if is_local(did) {
807+
local_def(self.tcx.map.get_parent(did.node))
808+
} else {
809+
// "tuple structs" with zero fields (such as
810+
// `pub struct Foo;`) don't have a ctor_id, hence
811+
// the unwrap_or to the same struct id.
812+
let maybe_did =
813+
csearch::get_tuple_struct_definition_if_ctor(
814+
&self.tcx.sess.cstore, did);
815+
maybe_did.unwrap_or(did)
816+
})
817+
}
818+
// Tuple struct constructors across crates are identified as
819+
// DefFn types, so we explicitly handle that case here.
820+
Some(&ast::DefFn(did, _)) if !is_local(did) => {
821+
match csearch::get_tuple_struct_definition_if_ctor(
822+
&self.tcx.sess.cstore, did) {
823+
Some(did) => guard(did),
824+
None => {}
825+
}
826+
}
827+
_ => {}
828+
}
829+
}
775830
_ => {}
776831
}
777832

@@ -821,15 +876,16 @@ impl<'a> Visitor<()> for PrivacyVisitor<'a> {
821876
match ty::get(ty::pat_ty(self.tcx, pattern)).sty {
822877
ty::ty_struct(id, _) => {
823878
for field in fields.iter() {
824-
self.check_field(pattern.span, id, field.ident);
879+
self.check_field(pattern.span, id,
880+
NamedField(field.ident));
825881
}
826882
}
827883
ty::ty_enum(_, _) => {
828884
match self.tcx.def_map.borrow().find(&pattern.id) {
829885
Some(&ast::DefVariant(_, variant_id, _)) => {
830886
for field in fields.iter() {
831887
self.check_field(pattern.span, variant_id,
832-
field.ident);
888+
NamedField(field.ident));
833889
}
834890
}
835891
_ => self.tcx.sess.span_bug(pattern.span,
@@ -844,6 +900,27 @@ impl<'a> Visitor<()> for PrivacyVisitor<'a> {
844900
struct type?!"),
845901
}
846902
}
903+
904+
// Patterns which bind no fields are allowable (the path is check
905+
// elsewhere).
906+
ast::PatEnum(_, Some(ref fields)) => {
907+
match ty::get(ty::pat_ty(self.tcx, pattern)).sty {
908+
ty::ty_struct(id, _) => {
909+
for (i, field) in fields.iter().enumerate() {
910+
match field.node {
911+
ast::PatWild(..) => continue,
912+
_ => {}
913+
}
914+
self.check_field(field.span, id, UnnamedField(i));
915+
}
916+
}
917+
ty::ty_enum(..) => {
918+
// enum fields have no privacy at this time
919+
}
920+
_ => {}
921+
}
922+
923+
}
847924
_ => {}
848925
}
849926

src/libsyntax/ast.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -493,10 +493,10 @@ pub enum Expr_ {
493493
ExprVstore(@Expr, ExprVstore),
494494
// First expr is the place; second expr is the value.
495495
ExprBox(@Expr, @Expr),
496-
ExprVec(Vec<@Expr> , Mutability),
497-
ExprCall(@Expr, Vec<@Expr> ),
498-
ExprMethodCall(Ident, Vec<P<Ty>> , Vec<@Expr> ),
499-
ExprTup(Vec<@Expr> ),
496+
ExprVec(Vec<@Expr>, Mutability),
497+
ExprCall(@Expr, Vec<@Expr>),
498+
ExprMethodCall(Ident, Vec<P<Ty>>, Vec<@Expr>),
499+
ExprTup(Vec<@Expr>),
500500
ExprBinary(BinOp, @Expr, @Expr),
501501
ExprUnary(UnOp, @Expr),
502502
ExprLit(@Lit),
@@ -508,14 +508,14 @@ pub enum Expr_ {
508508
// Conditionless loop (can be exited with break, cont, or ret)
509509
// FIXME #6993: change to Option<Name>
510510
ExprLoop(P<Block>, Option<Ident>),
511-
ExprMatch(@Expr, Vec<Arm> ),
511+
ExprMatch(@Expr, Vec<Arm>),
512512
ExprFnBlock(P<FnDecl>, P<Block>),
513513
ExprProc(P<FnDecl>, P<Block>),
514514
ExprBlock(P<Block>),
515515

516516
ExprAssign(@Expr, @Expr),
517517
ExprAssignOp(BinOp, @Expr, @Expr),
518-
ExprField(@Expr, Ident, Vec<P<Ty>> ),
518+
ExprField(@Expr, Ident, Vec<P<Ty>>),
519519
ExprIndex(@Expr, @Expr),
520520

521521
/// Expression that looks like a "name". For example,
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
pub struct A(());
12+
pub struct B(int);
13+
pub struct C(pub int, int);
14+
pub struct D(pub int);

0 commit comments

Comments
 (0)