From ff04aa8e385f343f66c5bed6a34d1ebf6c971e4d Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Mon, 24 Feb 2014 20:17:02 +1300 Subject: [PATCH] Allow inheritance between structs. No subtyping, no interaction with traits. Partially addresses #9912. --- src/doc/rust.md | 16 +++ src/librustc/front/config.rs | 2 + src/librustc/front/feature_gate.rs | 14 ++- src/librustc/metadata/common.rs | 117 +++++++++--------- src/librustc/metadata/decoder.rs | 10 +- src/librustc/metadata/encoder.rs | 63 +++++----- src/librustc/middle/privacy.rs | 7 +- src/librustc/middle/resolve.rs | 62 ++++++---- src/librustc/middle/ty.rs | 101 ++++++++------- src/librustc/middle/typeck/check/mod.rs | 71 +++++++++-- src/librustc/middle/typeck/collect.rs | 108 ++++++++++++++-- src/libsyntax/ast.rs | 4 +- src/libsyntax/fold.rs | 12 +- src/libsyntax/parse/parser.rs | 35 +++++- src/libsyntax/parse/token.rs | 29 ++--- src/libsyntax/print/pprust.rs | 10 ++ src/libsyntax/visit.rs | 4 + src/test/auxiliary/inherit_struct_lib.rs | 27 ++++ ...tax-class-traits.rs => inherit-struct1.rs} | 11 +- src/test/compile-fail/inherit-struct2.rs | 25 ++++ src/test/compile-fail/inherit-struct3.rs | 25 ++++ src/test/compile-fail/inherit-struct4.rs | 24 ++++ src/test/compile-fail/inherit-struct5.rs | 20 +++ src/test/compile-fail/inherit-struct6.rs | 42 +++++++ src/test/compile-fail/inherit-struct7.rs | 19 +++ src/test/compile-fail/inherit-struct8.rs | 31 +++++ src/test/compile-fail/inherit-struct9.rs | 18 +++ src/test/debug-info/simple-struct.rs | 18 ++- src/test/run-pass/inherit-struct1.rs | 61 +++++++++ src/test/run-pass/inherit-struct2.rs | 25 ++++ 30 files changed, 803 insertions(+), 208 deletions(-) create mode 100644 src/test/auxiliary/inherit_struct_lib.rs rename src/test/compile-fail/{removed-syntax-class-traits.rs => inherit-struct1.rs} (69%) create mode 100644 src/test/compile-fail/inherit-struct2.rs create mode 100644 src/test/compile-fail/inherit-struct3.rs create mode 100644 src/test/compile-fail/inherit-struct4.rs create mode 100644 src/test/compile-fail/inherit-struct5.rs create mode 100644 src/test/compile-fail/inherit-struct6.rs create mode 100644 src/test/compile-fail/inherit-struct7.rs create mode 100644 src/test/compile-fail/inherit-struct8.rs create mode 100644 src/test/compile-fail/inherit-struct9.rs create mode 100644 src/test/run-pass/inherit-struct1.rs create mode 100644 src/test/run-pass/inherit-struct2.rs diff --git a/src/doc/rust.md b/src/doc/rust.md index 17941a152094f..7b402cd4b16d4 100644 --- a/src/doc/rust.md +++ b/src/doc/rust.md @@ -1214,6 +1214,22 @@ struct Cookie; let c = [Cookie, Cookie, Cookie, Cookie]; ~~~~ +By using the `struct_inherit` feature gate, structures may use single inheritance. A Structure may only +inherit from a single other structure, called the _super-struct_. The inheriting structure (sub-struct) +acts as if all fields in the super-struct were present in the sub-struct. Fields declared in a sub-struct +must not have the same name as any field in any (transitive) super-struct. All fields (both declared +and inherited) must be specified in any initializers. Inheritance between structures does not give +subtyping or coercion. The super-struct and sub-struct must be defined in the same crate. The super-struct +must be declared using the `virtual` keyword. +For example: + +~~~~ {.ignore} +virtual struct Sup { x: int } +struct Sub : Sup { y: int } +let s = Sub {x: 10, y: 11}; +let sx = s.x; +~~~~ + ### Enumerations An _enumeration_ is a simultaneous definition of a nominal [enumerated type](#enumerated-types) as well as a set of *constructors*, diff --git a/src/librustc/front/config.rs b/src/librustc/front/config.rs index 703ff51b3b0e2..e630d7e15e039 100644 --- a/src/librustc/front/config.rs +++ b/src/librustc/front/config.rs @@ -151,6 +151,8 @@ fn fold_struct(cx: &Context, def: &ast::StructDef) -> @ast::StructDef { @ast::StructDef { fields: fields.collect(), ctor_id: def.ctor_id, + super_struct: def.super_struct.clone(), + is_virtual: def.is_virtual, } } diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index 3ab39dd121f08..0f76075656e02 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -55,6 +55,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("default_type_params", Active), ("quote", Active), ("linkage", Active), + ("struct_inherit", Active), // These are used to test this portion of the compiler, they don't actually // mean anything @@ -190,11 +191,22 @@ impl<'a> Visitor<()> for Context<'a> { } } - ast::ItemStruct(..) => { + ast::ItemStruct(struct_definition, _) => { if attr::contains_name(i.attrs.as_slice(), "simd") { self.gate_feature("simd", i.span, "SIMD types are experimental and possibly buggy"); } + match struct_definition.super_struct { + Some(ref path) => self.gate_feature("struct_inherit", path.span, + "struct inheritance is experimental \ + and possibly buggy"), + None => {} + } + if struct_definition.is_virtual { + self.gate_feature("struct_inherit", i.span, + "struct inheritance (`virtual` keyword) is \ + experimental and possibly buggy"); + } } _ => {} diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 264829b18a309..4a0b3ea0cf6a0 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -92,6 +92,7 @@ pub static tag_path_len: uint = 0x25; pub static tag_path_elem_mod: uint = 0x26; pub static tag_path_elem_name: uint = 0x27; pub static tag_item_field: uint = 0x28; +pub static tag_item_field_origin: uint = 0x29; pub static tag_item_variances: uint = 0x2a; /* @@ -102,43 +103,43 @@ pub static tag_item_variances: uint = 0x2a; both, tag_item_trait_method and tag_item_impl_method have to be two different tags. */ -pub static tag_item_impl_method: uint = 0x2c; -pub static tag_item_trait_method_explicit_self: uint = 0x2d; +pub static tag_item_impl_method: uint = 0x30; +pub static tag_item_trait_method_explicit_self: uint = 0x31; // Reexports are found within module tags. Each reexport contains def_ids // and names. -pub static tag_items_data_item_reexport: uint = 0x2f; -pub static tag_items_data_item_reexport_def_id: uint = 0x30; -pub static tag_items_data_item_reexport_name: uint = 0x31; +pub static tag_items_data_item_reexport: uint = 0x38; +pub static tag_items_data_item_reexport_def_id: uint = 0x39; +pub static tag_items_data_item_reexport_name: uint = 0x3a; // used to encode crate_ctxt side tables #[deriving(Eq)] #[repr(uint)] -pub enum astencode_tag { // Reserves 0x32 -- 0x45 - tag_ast = 0x32, - - tag_tree = 0x33, - - tag_id_range = 0x34, - - tag_table = 0x35, - tag_table_id = 0x36, - tag_table_val = 0x37, - tag_table_def = 0x38, - tag_table_node_type = 0x39, - tag_table_node_type_subst = 0x3a, - tag_table_freevars = 0x3b, - tag_table_tcache = 0x3c, - tag_table_param_defs = 0x3d, - tag_table_mutbl = 0x3e, - tag_table_last_use = 0x3f, - tag_table_spill = 0x40, - tag_table_method_map = 0x41, - tag_table_vtable_map = 0x42, - tag_table_adjustments = 0x43, - tag_table_moves_map = 0x44, - tag_table_capture_map = 0x45 +pub enum astencode_tag { // Reserves 0x40 -- 0x5f + tag_ast = 0x40, + + tag_tree = 0x41, + + tag_id_range = 0x42, + + tag_table = 0x43, + tag_table_id = 0x44, + tag_table_val = 0x45, + tag_table_def = 0x46, + tag_table_node_type = 0x47, + tag_table_node_type_subst = 0x48, + tag_table_freevars = 0x49, + tag_table_tcache = 0x4a, + tag_table_param_defs = 0x4b, + tag_table_mutbl = 0x4c, + tag_table_last_use = 0x4d, + tag_table_spill = 0x4e, + tag_table_method_map = 0x4f, + tag_table_vtable_map = 0x50, + tag_table_adjustments = 0x51, + tag_table_moves_map = 0x52, + tag_table_capture_map = 0x53 } static first_astencode_tag: uint = tag_ast as uint; static last_astencode_tag: uint = tag_table_capture_map as uint; @@ -151,9 +152,9 @@ impl astencode_tag { } } -pub static tag_item_trait_method_sort: uint = 0x46; +pub static tag_item_trait_method_sort: uint = 0x60; -pub static tag_item_impl_type_basename: uint = 0x47; +pub static tag_item_impl_type_basename: uint = 0x61; // Language items are a top-level directory (for speed). Hierarchy: // @@ -162,42 +163,42 @@ pub static tag_item_impl_type_basename: uint = 0x47; // - tag_lang_items_item_id: u32 // - tag_lang_items_item_node_id: u32 -pub static tag_lang_items: uint = 0x48; -pub static tag_lang_items_item: uint = 0x49; -pub static tag_lang_items_item_id: uint = 0x4a; -pub static tag_lang_items_item_node_id: uint = 0x4b; +pub static tag_lang_items: uint = 0x70; +pub static tag_lang_items_item: uint = 0x71; +pub static tag_lang_items_item_id: uint = 0x72; +pub static tag_lang_items_item_node_id: uint = 0x73; -pub static tag_item_unnamed_field: uint = 0x4c; -pub static tag_items_data_item_visibility: uint = 0x4e; +pub static tag_item_unnamed_field: uint = 0x74; +pub static tag_items_data_item_visibility: uint = 0x76; -pub static tag_item_method_tps: uint = 0x51; -pub static tag_item_method_fty: uint = 0x52; +pub static tag_item_method_tps: uint = 0x79; +pub static tag_item_method_fty: uint = 0x7a; -pub static tag_mod_child: uint = 0x53; -pub static tag_misc_info: uint = 0x54; -pub static tag_misc_info_crate_items: uint = 0x55; +pub static tag_mod_child: uint = 0x7b; +pub static tag_misc_info: uint = 0x7c; +pub static tag_misc_info_crate_items: uint = 0x7d; -pub static tag_item_method_provided_source: uint = 0x56; -pub static tag_item_impl_vtables: uint = 0x57; +pub static tag_item_method_provided_source: uint = 0x7e; +pub static tag_item_impl_vtables: uint = 0x7f; -pub static tag_impls: uint = 0x58; -pub static tag_impls_impl: uint = 0x59; +pub static tag_impls: uint = 0x80; +pub static tag_impls_impl: uint = 0x81; -pub static tag_items_data_item_inherent_impl: uint = 0x5a; -pub static tag_items_data_item_extension_impl: uint = 0x5b; +pub static tag_items_data_item_inherent_impl: uint = 0x82; +pub static tag_items_data_item_extension_impl: uint = 0x83; -pub static tag_region_param_def: uint = 0x5c; -pub static tag_region_param_def_ident: uint = 0x5d; -pub static tag_region_param_def_def_id: uint = 0x5e; +pub static tag_region_param_def: uint = 0x84; +pub static tag_region_param_def_ident: uint = 0x85; +pub static tag_region_param_def_def_id: uint = 0x86; -pub static tag_native_libraries: uint = 0x5f; -pub static tag_native_libraries_lib: uint = 0x60; -pub static tag_native_libraries_name: uint = 0x61; -pub static tag_native_libraries_kind: uint = 0x62; +pub static tag_native_libraries: uint = 0x87; +pub static tag_native_libraries_lib: uint = 0x88; +pub static tag_native_libraries_name: uint = 0x89; +pub static tag_native_libraries_kind: uint = 0x8a; -pub static tag_macro_registrar_fn: uint = 0x63; -pub static tag_exported_macros: uint = 0x64; -pub static tag_macro_def: uint = 0x65; +pub static tag_macro_registrar_fn: uint = 0x8b; +pub static tag_exported_macros: uint = 0x8c; +pub static tag_macro_def: uint = 0x8d; #[deriving(Clone, Show)] pub struct LinkMeta { diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index ee7ce817a3f20..b1cede8e410c4 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -975,21 +975,27 @@ pub fn get_struct_fields(intr: Rc, cdata: Cmd, id: ast::NodeId) // FIXME #6993: name should be of type Name, not Ident let name = item_name(&*intr, an_item); let did = item_def_id(an_item, cdata); + let tagdoc = reader::get_doc(an_item, tag_item_field_origin); + let origin_id = translate_def_id(cdata, reader::with_doc_data(tagdoc, parse_def_id)); result.push(ty::field_ty { name: name.name, - id: did, vis: - struct_field_family_to_visibility(f), + id: did, + vis: struct_field_family_to_visibility(f), + origin: origin_id, }); } true }); reader::tagged_docs(item, tag_item_unnamed_field, |an_item| { let did = item_def_id(an_item, cdata); + let tagdoc = reader::get_doc(an_item, tag_item_field_origin); let f = item_family(an_item); + let origin_id = translate_def_id(cdata, reader::with_doc_data(tagdoc, parse_def_id)); result.push(ty::field_ty { name: special_idents::unnamed_field.name, id: did, vis: struct_field_family_to_visibility(f), + origin: origin_id, }); true }); diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 085a96ea7be2f..f4c3f5640682f 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -290,23 +290,22 @@ fn encode_parent_item(ebml_w: &mut Encoder, id: DefId) { } fn encode_struct_fields(ebml_w: &mut Encoder, - def: @StructDef) { - for f in def.fields.iter() { - match f.node.kind { - NamedField(ident, vis) => { - ebml_w.start_tag(tag_item_field); - encode_struct_field_family(ebml_w, vis); - encode_name(ebml_w, ident.name); - encode_def_id(ebml_w, local_def(f.node.id)); - ebml_w.end_tag(); - } - UnnamedField(vis) => { - ebml_w.start_tag(tag_item_unnamed_field); - encode_struct_field_family(ebml_w, vis); - encode_def_id(ebml_w, local_def(f.node.id)); - ebml_w.end_tag(); - } + fields: &Vec, + origin: DefId) { + for f in fields.iter() { + if f.name == special_idents::unnamed_field.name { + ebml_w.start_tag(tag_item_unnamed_field); + } else { + ebml_w.start_tag(tag_item_field); + encode_name(ebml_w, f.name); } + encode_struct_field_family(ebml_w, f.vis); + encode_def_id(ebml_w, f.id); + ebml_w.start_tag(tag_item_field_origin); + let s = def_to_str(origin); + ebml_w.writer.write(s.as_bytes()); + ebml_w.end_tag(); + ebml_w.end_tag(); } } @@ -344,12 +343,13 @@ fn encode_enum_variant_info(ecx: &EncodeContext, encode_symbol(ecx, ebml_w, variant.node.id); } ast::TupleVariantKind(_) => {}, - ast::StructVariantKind(def) => { + ast::StructVariantKind(_) => { + let fields = ty::lookup_struct_fields(ecx.tcx, def_id); let idx = encode_info_for_struct(ecx, ebml_w, - def.fields.as_slice(), + &fields, index); - encode_struct_fields(ebml_w, def); + encode_struct_fields(ebml_w, &fields, def_id); let bkts = create_index(idx); encode_index(ebml_w, bkts, write_i64); } @@ -666,7 +666,7 @@ fn encode_provided_source(ebml_w: &mut Encoder, /* Returns an index of items in this class */ fn encode_info_for_struct(ecx: &EncodeContext, ebml_w: &mut Encoder, - fields: &[StructField], + fields: &Vec, global_index: @RefCell> >) -> Vec> { /* Each class has its own index, since different classes @@ -676,12 +676,9 @@ fn encode_info_for_struct(ecx: &EncodeContext, /* We encode both private and public fields -- need to include private fields to get the offsets right */ for field in fields.iter() { - let (nm, vis) = match field.node.kind { - NamedField(nm, vis) => (nm, vis), - UnnamedField(vis) => (special_idents::unnamed_field, vis) - }; + let nm = field.name; + let id = field.id.node; - let id = field.node.id; index.push(entry {val: id as i64, pos: ebml_w.writer.tell().unwrap()}); global_index.borrow_mut().push(entry { val: id as i64, @@ -689,9 +686,9 @@ fn encode_info_for_struct(ecx: &EncodeContext, }); ebml_w.start_tag(tag_items_data_item); debug!("encode_info_for_struct: doing {} {}", - token::get_ident(nm), id); - encode_struct_field_family(ebml_w, vis); - encode_name(ebml_w, nm.name); + token::get_name(nm), id); + encode_struct_field_family(ebml_w, field.vis); + encode_name(ebml_w, nm); encode_type(ecx, ebml_w, node_id_to_type(tcx, id)); encode_def_id(ebml_w, local_def(id)); ebml_w.end_tag(); @@ -983,12 +980,16 @@ fn encode_info_for_item(ecx: &EncodeContext, generics); } ItemStruct(struct_def, _) => { + let fields = ty::lookup_struct_fields(tcx, def_id); + /* First, encode the fields These come first because we need to write them to make the index, and the index needs to be in the item for the class itself */ - let idx = encode_info_for_struct(ecx, ebml_w, - struct_def.fields.as_slice(), index); + let idx = encode_info_for_struct(ecx, + ebml_w, + &fields, + index); /* Index the class*/ add_to_index(item, ebml_w, index); @@ -1008,7 +1009,7 @@ fn encode_info_for_item(ecx: &EncodeContext, /* Encode def_ids for each field and method for methods, write all the stuff get_trait_method needs to know*/ - encode_struct_fields(ebml_w, struct_def); + encode_struct_fields(ebml_w, &fields, def_id); (ecx.encode_inlined_item)(ecx, ebml_w, IIItemRef(item)); diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index b2e1a992f5479..59d4c2e03d499 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -488,7 +488,7 @@ impl<'a> PrivacyVisitor<'a> { // members, so that's why we test the parent, and not the did itself. let mut cur = self.curitem; loop { - debug!("privacy - questioning {}", self.nodestr(cur)); + debug!("privacy - questioning {}, {:?}", self.nodestr(cur), cur); match cur { // If the relevant parent is in our history, then we're allowed // to look inside any of our ancestor's immediate private items, @@ -554,11 +554,14 @@ impl<'a> PrivacyVisitor<'a> { } // Checks that a field is in scope. - fn check_field(&mut self, span: Span, id: ast::DefId, + fn check_field(&mut self, + span: Span, + id: ast::DefId, name: FieldName) { let fields = ty::lookup_struct_fields(self.tcx, id); let field = match name { NamedField(ident) => { + debug!("privacy - check named field {} in struct {}", ident.name, id); fields.iter().find(|f| f.name == ident.name).unwrap() } UnnamedField(idx) => fields.get(idx) diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 70483aac7062d..9112434b12c83 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -3708,6 +3708,7 @@ impl<'a> Resolver<'a> { ItemStruct(ref struct_def, ref generics) => { self.resolve_struct(item.id, generics, + struct_def.super_struct, struct_def.fields.as_slice()); } @@ -3922,30 +3923,10 @@ impl<'a> Resolver<'a> { } fn resolve_struct(&mut self, - id: NodeId, - generics: &Generics, - fields: &[StructField]) { - let mut ident_map: HashMap = HashMap::new(); - for field in fields.iter() { - match field.node.kind { - NamedField(ident, _) => { - match ident_map.find(&ident) { - Some(&prev_field) => { - let ident_str = token::get_ident(ident); - self.resolve_error(field.span, - format!("field `{}` is already declared", ident_str)); - self.session.span_note(prev_field.span, - "previously declared here"); - }, - None => { - ident_map.insert(ident, field); - } - } - } - _ => () - } - } - + id: NodeId, + generics: &Generics, + super_struct: Option>, + fields: &[StructField]) { // If applicable, create a rib for the type parameters. self.with_type_parameter_rib(HasTypeParameters(generics, id, @@ -3955,6 +3936,39 @@ impl<'a> Resolver<'a> { // Resolve the type parameters. this.resolve_type_parameters(&generics.ty_params); + // Resolve the super struct. + match super_struct { + Some(t) => match t.node { + TyPath(ref path, None, path_id) => { + match this.resolve_path(id, path, TypeNS, true) { + Some((DefTy(def_id), lp)) if this.structs.contains(&def_id) => { + let def = DefStruct(def_id); + debug!("(resolving struct) resolved `{}` to type {:?}", + token::get_ident(path.segments + .last().unwrap() + .identifier), + def); + debug!("(resolving struct) writing resolution for `{}` (id {})", + this.path_idents_to_str(path), + path_id); + this.record_def(path_id, (def, lp)); + } + Some((DefStruct(_), _)) => { + this.session.span_err(t.span, + "super-struct is defined \ + in a different crate") + }, + Some(_) => this.session.span_err(t.span, + "super-struct is not a struct type"), + None => this.session.span_err(t.span, + "super-struct could not be resolved"), + } + }, + _ => this.session.span_bug(t.span, "path not mapped to a TyPath") + }, + None => {} + } + // Resolve fields. for field in fields.iter() { this.resolve_type(field.node.ty); diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index d6f1866f5dc03..e32ab1fc8e39a 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -52,7 +52,6 @@ use syntax::parse::token::InternedString; use syntax::{ast, ast_map}; use syntax::owned_slice::OwnedSlice; use syntax::abi; -use syntax; use collections::enum_set::{EnumSet, CLike}; pub type Disr = u64; @@ -150,10 +149,12 @@ pub enum TraitStore { RegionTraitStore(Region, ast::Mutability), } +#[deriving(Clone)] pub struct field_ty { pub name: Name, pub id: DefId, pub vis: ast::Visibility, + pub origin: ast::DefId, // The DefId of the struct in which the field is declared. } // Contains information needed to resolve types and (in the future) look up @@ -303,6 +304,8 @@ pub struct ctxt { // A mapping of fake provided method def_ids to the default implementation pub provided_method_sources: RefCell>, pub supertraits: RefCell >>, + pub superstructs: RefCell>>, + pub struct_fields: RefCell>>, // Maps from def-id of a type or region parameter to its // (inferred) variance. @@ -1115,6 +1118,8 @@ pub fn mk_ctxt(s: Session, lang_items: lang_items, provided_method_sources: RefCell::new(DefIdMap::new()), supertraits: RefCell::new(DefIdMap::new()), + superstructs: RefCell::new(DefIdMap::new()), + struct_fields: RefCell::new(DefIdMap::new()), destructor_for_type: RefCell::new(DefIdMap::new()), destructors: RefCell::new(DefIdSet::new()), trait_impls: RefCell::new(DefIdMap::new()), @@ -3987,60 +3992,70 @@ pub fn lookup_field_type(tcx: &ctxt, subst(tcx, substs, t) } -// Look up the list of field names and IDs for a given struct +// Lookup all ancestor structs of a struct indicated by did. That is the reflexive, +// transitive closure of doing a single lookup in cx.superstructs. +fn lookup_super_structs(cx: &ctxt, + did: ast::DefId) -> Vec { + let mut this_result: Vec = vec!(did); + match cx.superstructs.borrow().find(&did) { + Some(&Some(def_id)) => { + let ss: Vec = lookup_super_structs(cx, def_id); + this_result.extend(ss.move_iter()); + this_result + }, + Some(&None) => this_result, + None => { + cx.sess.bug( + format!("ID not mapped to super-struct: {}", + cx.map.node_to_str(did.node))); + } + } +} + +// Look up the list of field names and IDs for a given struct. // Fails if the id is not bound to a struct. pub fn lookup_struct_fields(cx: &ctxt, did: ast::DefId) -> Vec { if did.krate == ast::LOCAL_CRATE { - match cx.map.find(did.node) { - Some(ast_map::NodeItem(i)) => { - match i.node { - ast::ItemStruct(struct_def, _) => { - struct_field_tys(struct_def.fields.as_slice()) - } - _ => cx.sess.bug("struct ID bound to non-struct") - } - } - Some(ast_map::NodeVariant(ref variant)) => { - match (*variant).node.kind { - ast::StructVariantKind(struct_def) => { - struct_field_tys(struct_def.fields.as_slice()) - } - _ => { - cx.sess.bug("struct ID bound to enum variant that \ - isn't struct-like") - } + // We store the fields which are syntactically in each struct in cx. So + // we have to walk the inheritance chain of the struct to get all the + // structs (explicit and inherited) for a struct. If this is expensive + // we could cache the whole list of fields here. + let structs = lookup_super_structs(cx, did); + let struct_fields = cx.struct_fields.borrow(); + let results: Vec<&@Vec> = structs.iter().map(|s| { + match struct_fields.find(s) { + Some(fields) => fields, + _ => { + cx.sess.bug( + format!("ID not mapped to struct fields: {}", + cx.map.node_to_str(did.node))); } } - _ => { - cx.sess.bug( - format!("struct ID not bound to an item: {}", - cx.map.node_to_str(did.node))); + }).collect(); + + let len = results.iter().map(|x| x.len()).fold(0, |a, b| a + b); + let mut result: Vec = Vec::with_capacity(len); + for rs in results.iter() { + for r in rs.iter() { + result.push(*r); } } + assert!(result.len() == len); + result } else { csearch::get_struct_fields(&cx.sess.cstore, did) } } -fn struct_field_tys(fields: &[StructField]) -> Vec { - fields.iter().map(|field| { - match field.node.kind { - NamedField(ident, visibility) => { - field_ty { - name: ident.name, - id: ast_util::local_def(field.node.id), - vis: visibility, - } - } - UnnamedField(visibility) => { - field_ty { - name: syntax::parse::token::special_idents::unnamed_field.name, - id: ast_util::local_def(field.node.id), - vis: visibility, - } - } - } - }).collect() +pub fn lookup_struct_field(cx: &ctxt, + parent: ast::DefId, + field_id: ast::DefId) + -> field_ty { + let r = lookup_struct_fields(cx, parent); + match r.iter().find(|f| f.id.node == field_id.node) { + Some(t) => *t, + None => cx.sess.bug("struct ID not found in parent's fields") + } } // Returns a list of fields corresponding to the struct's items. trans uses diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index d971b2c31c229..f640b5dc9be0f 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -122,6 +122,7 @@ use std::vec::Vec; use syntax::abi; use syntax::ast::{Provided, Required}; use syntax::ast; +use syntax::ast_map; use syntax::ast_util::local_def; use syntax::ast_util; use syntax::attr; @@ -509,6 +510,57 @@ fn check_fn<'a>(ccx: &'a CrateCtxt<'a>, fcx } +fn span_for_field(tcx: &ty::ctxt, field: &ty::field_ty, struct_id: ast::DefId) -> Span { + assert!(field.id.krate == ast::LOCAL_CRATE); + let item = match tcx.map.find(struct_id.node) { + Some(ast_map::NodeItem(item)) => item, + None => fail!("node not in ast map: {}", struct_id.node), + _ => fail!("expected item, found {}", tcx.map.node_to_str(struct_id.node)) + }; + + match item.node { + ast::ItemStruct(struct_def, _) => { + match struct_def.fields.iter().find(|f| match f.node.kind { + ast::NamedField(ident, _) => ident.name == field.name, + _ => false, + }) { + Some(f) => f.span, + None => tcx.sess.bug(format!("Could not find field {}", + token::get_name(field.name))), + } + }, + _ => tcx.sess.bug("Field found outside of a struct?"), + } +} + +// Check struct fields are uniquely named wrt parents. +fn check_for_field_shadowing(tcx: &ty::ctxt, + id: ast::DefId) { + let struct_fields = tcx.struct_fields.borrow(); + let fields = struct_fields.get(&id); + + let superstructs = tcx.superstructs.borrow(); + let super_struct = superstructs.get(&id); + match *super_struct { + Some(parent_id) => { + let super_fields = ty::lookup_struct_fields(tcx, parent_id); + for f in fields.iter() { + match super_fields.iter().find(|sf| f.name == sf.name) { + Some(prev_field) => { + tcx.sess.span_err(span_for_field(tcx, f, id), + format!("field `{}` hides field declared in super-struct", + token::get_name(f.name))); + tcx.sess.span_note(span_for_field(tcx, prev_field, parent_id), + "previously declared here"); + }, + None => {} + } + } + }, + None => {} + } +} + pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) { let tcx = ccx.tcx; @@ -518,6 +570,9 @@ pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) { // Check that the struct is instantiable check_instantiable(tcx, span, id); + // Check there are no overlapping fields in super-structs + check_for_field_shadowing(tcx, local_def(id)); + if ty::lookup_simd(tcx, local_def(id)) { check_simd(tcx, span, id); } @@ -2385,14 +2440,14 @@ fn check_expr_with_unifier(fcx: &FnCtxt, // Look up and check the fields. let class_fields = ty::lookup_struct_fields(tcx, class_id); check_struct_or_variant_fields(fcx, - struct_type, - span, - class_id, - id, - substitutions, - class_fields.as_slice(), - fields, - base_expr.is_none()); + struct_type, + span, + class_id, + id, + substitutions, + class_fields.as_slice(), + fields, + base_expr.is_none()); if ty::type_is_error(fcx.node_ty(id)) { struct_type = ty::mk_err(); } diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 6df8da3edaa69..490a52df2ba7a 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -46,7 +46,7 @@ use util::ppaux; use util::ppaux::Repr; use std::rc::Rc; -use collections::HashSet; +use collections::{HashMap, HashSet}; use syntax::abi; use syntax::ast::{RegionTyParamBound, TraitTyParamBound}; @@ -440,15 +440,35 @@ pub fn ensure_supertraits(ccx: &CrateCtxt, pub fn convert_field(ccx: &CrateCtxt, struct_generics: &ty::Generics, - v: &ast::StructField) { + v: &ast::StructField, + origin: ast::DefId) -> ty::field_ty { let tt = ccx.to_ty(&ExplicitRscope, v.node.ty); write_ty_to_tcx(ccx.tcx, v.node.id, tt); /* add the field to the tcache */ ccx.tcx.tcache.borrow_mut().insert(local_def(v.node.id), - ty::ty_param_bounds_and_ty { - generics: struct_generics.clone(), - ty: tt - }); + ty::ty_param_bounds_and_ty { + generics: struct_generics.clone(), + ty: tt + }); + + match v.node.kind { + ast::NamedField(ident, visibility) => { + ty::field_ty { + name: ident.name, + id: local_def(v.node.id), + vis: visibility, + origin: origin, + } + } + ast::UnnamedField(visibility) => { + ty::field_ty { + name: special_idents::unnamed_field.name, + id: local_def(v.node.id), + vis: visibility, + origin: origin, + } + } + } } fn convert_methods(ccx: &CrateCtxt, @@ -637,12 +657,21 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) { ast::ItemStruct(struct_def, ref generics) => { ensure_no_ty_param_bounds(ccx, it.span, generics, "structure"); - // Write the class type + // Write the class type. let tpt = ty_of_item(ccx, it); write_ty_to_tcx(tcx, it.id, tpt.ty); tcx.tcache.borrow_mut().insert(local_def(it.id), tpt.clone()); + // Write the super-struct type, if it exists. + match struct_def.super_struct { + Some(ty) => { + let supserty = ccx.to_ty(&ExplicitRscope, ty); + write_ty_to_tcx(tcx, it.id, supserty); + }, + _ => {}, + } + convert_struct(ccx, struct_def, tpt, it.id); }, ast::ItemTy(_, ref generics) => { @@ -671,10 +700,67 @@ pub fn convert_struct(ccx: &CrateCtxt, id: ast::NodeId) { let tcx = ccx.tcx; - // Write the type of each of the members - for f in struct_def.fields.iter() { - convert_field(ccx, &tpt.generics, f); - } + // Write the type of each of the members and check for duplicate fields. + let mut seen_fields: HashMap = HashMap::new(); + let field_tys = struct_def.fields.iter().map(|f| { + let result = convert_field(ccx, &tpt.generics, f, local_def(id)); + + if result.name != special_idents::unnamed_field.name { + let dup = match seen_fields.find(&result.name) { + Some(prev_span) => { + tcx.sess.span_err(f.span, + format!("field `{}` is already declared", token::get_name(result.name))); + tcx.sess.span_note(*prev_span, + "previously declared here"); + true + }, + None => false, + }; + // FIXME(#6393) this whole dup thing is just to satisfy + // the borrow checker :-( + if !dup { + seen_fields.insert(result.name, f.span); + } + } + + result + }).collect(); + + tcx.struct_fields.borrow_mut().insert(local_def(id), @field_tys); + + let super_struct = match struct_def.super_struct { + Some(t) => match t.node { + ast::TyPath(_, _, path_id) => { + let def_map = tcx.def_map.borrow(); + match def_map.find(&path_id) { + Some(&ast::DefStruct(def_id)) => { + // FIXME(#12511) Check for cycles in the inheritance hierarchy. + // Check super-struct is virtual. + match tcx.map.find(def_id.node) { + Some(ast_map::NodeItem(i)) => match i.node { + ast::ItemStruct(struct_def, _) => { + if !struct_def.is_virtual { + tcx.sess.span_err(t.span, + "struct inheritance is only \ + allowed from virtual structs"); + } + }, + _ => {}, + }, + _ => {}, + } + + Some(def_id) + }, + _ => None, + } + } + _ => None, + }, + None => None, + }; + tcx.superstructs.borrow_mut().insert(local_def(id), super_struct); + let substs = mk_item_substs(ccx, &tpt.generics, None); let selfty = ty::mk_struct(tcx, local_def(id), substs); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 33c0f2c46bb5e..2b6f94e6bf518 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1080,7 +1080,9 @@ pub struct StructDef { pub fields: Vec, /* fields, not including ctor */ /* ID of the constructor. This is only used for tuple- or enum-like * structs. */ - pub ctor_id: Option + pub ctor_id: Option, + pub super_struct: Option>, // Super struct, if specified. + pub is_virtual: bool, // True iff the struct may be inherited from. } /* diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index fc4f427d8d738..73ad2664be4c0 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -235,7 +235,12 @@ pub trait Folder { kind = StructVariantKind(@ast::StructDef { fields: struct_def.fields.iter() .map(|f| self.fold_struct_field(f)).collect(), - ctor_id: struct_def.ctor_id.map(|c| self.new_id(c)) + ctor_id: struct_def.ctor_id.map(|c| self.new_id(c)), + super_struct: match struct_def.super_struct { + Some(t) => Some(self.fold_ty(t)), + None => None + }, + is_virtual: struct_def.is_virtual, }) } } @@ -480,6 +485,11 @@ fn fold_struct_def(struct_def: @StructDef, fld: &mut T) -> @StructDef @ast::StructDef { fields: struct_def.fields.iter().map(|f| fold_struct_field(f, fld)).collect(), ctor_id: struct_def.ctor_id.map(|cid| fld.new_id(cid)), + super_struct: match struct_def.super_struct { + Some(t) => Some(fld.fold_ty(t)), + None => None + }, + is_virtual: struct_def.is_virtual, } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 58634be199566..3d09147d8f302 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3884,11 +3884,26 @@ impl<'a> Parser<'a> { } // parse struct Foo { ... } - fn parse_item_struct(&mut self) -> ItemInfo { + fn parse_item_struct(&mut self, is_virtual: bool) -> ItemInfo { let class_name = self.parse_ident(); let generics = self.parse_generics(); - let mut fields: Vec ; + let super_struct = if self.eat(&token::COLON) { + let ty = self.parse_ty(false); + match ty.node { + TyPath(_, None, _) => { + Some(ty) + } + _ => { + self.span_err(ty.span, "not a struct"); + None + } + } + } else { + None + }; + + let mut fields: Vec; let is_tuple_like; if self.eat(&token::LBRACE) { @@ -3938,7 +3953,9 @@ impl<'a> Parser<'a> { (class_name, ItemStruct(@ast::StructDef { fields: fields, - ctor_id: if is_tuple_like { Some(new_id) } else { None } + ctor_id: if is_tuple_like { Some(new_id) } else { None }, + super_struct: super_struct, + is_virtual: is_virtual, }, generics), None) } @@ -4329,7 +4346,9 @@ impl<'a> Parser<'a> { return @ast::StructDef { fields: fields, - ctor_id: None + ctor_id: None, + super_struct: None, + is_virtual: false, }; } @@ -4514,6 +4533,12 @@ impl<'a> Parser<'a> { format!("expected `\\{` or `fn` but found `{}`", token_str)); } + let is_virtual = self.eat_keyword(keywords::Virtual); + if is_virtual && !self.is_keyword(keywords::Struct) { + self.span_err(self.span, + "`virtual` keyword may only be used with `struct`"); + } + // the rest are all guaranteed to be items: if self.is_keyword(keywords::Static) { // STATIC ITEM @@ -4614,7 +4639,7 @@ impl<'a> Parser<'a> { } if self.eat_keyword(keywords::Struct) { // STRUCT ITEM - let (ident, item_, extra_attrs) = self.parse_item_struct(); + let (ident, item_, extra_attrs) = self.parse_item_struct(is_virtual); let item = self.mk_item(lo, self.last_span.hi, ident, diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 77743cdb9dfcb..611ce7cc52798 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -474,22 +474,23 @@ declare_special_idents_and_keywords! { (35, Type, "type"); (36, Unsafe, "unsafe"); (37, Use, "use"); - (38, While, "while"); - (39, Continue, "continue"); - (40, Proc, "proc"); - (41, Box, "box"); + (38, Virtual, "virtual"); + (39, While, "while"); + (40, Continue, "continue"); + (41, Proc, "proc"); + (42, Box, "box"); 'reserved: - (42, Alignof, "alignof"); - (43, Be, "be"); - (44, Offsetof, "offsetof"); - (45, Priv, "priv"); - (46, Pure, "pure"); - (47, Sizeof, "sizeof"); - (48, Typeof, "typeof"); - (49, Unsized, "unsized"); - (50, Yield, "yield"); - (51, Do, "do"); + (43, Alignof, "alignof"); + (44, Be, "be"); + (45, Offsetof, "offsetof"); + (46, Priv, "priv"); + (47, Pure, "pure"); + (48, Sizeof, "sizeof"); + (49, Typeof, "typeof"); + (50, Unsized, "unsized"); + (51, Yield, "yield"); + (52, Do, "do"); } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 7041a6585a0cd..f4e337e2048f3 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -640,6 +640,9 @@ impl<'a> State<'a> { )); } ast::ItemStruct(struct_def, ref generics) => { + if struct_def.is_virtual { + try!(self.word_space("virtual")); + } try!(self.head(visibility_qualified(item.vis, "struct"))); try!(self.print_struct(struct_def, generics, item.ident, item.span)); } @@ -754,6 +757,13 @@ impl<'a> State<'a> { span: codemap::Span) -> IoResult<()> { try!(self.print_ident(ident)); try!(self.print_generics(generics)); + match struct_def.super_struct { + Some(t) => { + try!(self.word_space(":")); + try!(self.print_type(t)); + }, + None => {}, + } if ast_util::struct_def_is_tuple_like(struct_def) { if !struct_def.fields.is_empty() { try!(self.popen()); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 266de67b81d7e..1f75c2e062ff0 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -571,6 +571,10 @@ pub fn walk_struct_def>(visitor: &mut V, _: &Generics, _: NodeId, env: E) { + match struct_definition.super_struct { + Some(t) => visitor.visit_ty(t, env.clone()), + None => {}, + } for field in struct_definition.fields.iter() { visitor.visit_struct_field(field, env.clone()) } diff --git a/src/test/auxiliary/inherit_struct_lib.rs b/src/test/auxiliary/inherit_struct_lib.rs new file mode 100644 index 0000000000000..fd049a25a0cff --- /dev/null +++ b/src/test/auxiliary/inherit_struct_lib.rs @@ -0,0 +1,27 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test struct inheritance on structs from another crate. +#![feature(struct_inherit)] + +pub virtual struct S1 { + pub f1: int, +} + +pub struct S2 : S1 { + pub f2: int, +} + +pub fn test_s2(s2: S2) { + assert!(s2.f1 == 115); + assert!(s2.f2 == 113); +} + +pub static glob_s: S2 = S2 { f1: 32, f2: -45 }; diff --git a/src/test/compile-fail/removed-syntax-class-traits.rs b/src/test/compile-fail/inherit-struct1.rs similarity index 69% rename from src/test/compile-fail/removed-syntax-class-traits.rs rename to src/test/compile-fail/inherit-struct1.rs index 45b648560da05..00ea4b7783b9c 100644 --- a/src/test/compile-fail/removed-syntax-class-traits.rs +++ b/src/test/compile-fail/inherit-struct1.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,7 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct q : r { - //~^ ERROR expected `{`, `(`, or `;` after struct name - foo: int +// Test struct inheritance. +#![feature(struct_inherit)] + +struct S6 : ~S2; //~ ERROR not a struct + +pub fn main() { } diff --git a/src/test/compile-fail/inherit-struct2.rs b/src/test/compile-fail/inherit-struct2.rs new file mode 100644 index 0000000000000..99fd2d2f69d81 --- /dev/null +++ b/src/test/compile-fail/inherit-struct2.rs @@ -0,0 +1,25 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test struct inheritance. +#![feature(struct_inherit)] + +struct S2 : S0 { //~ ERROR super-struct could not be resolved + f2: int, +} + +trait T {} + +struct S3 : T { //~ ERROR super-struct is not a struct type + f3: int, +} + +pub fn main() { +} diff --git a/src/test/compile-fail/inherit-struct3.rs b/src/test/compile-fail/inherit-struct3.rs new file mode 100644 index 0000000000000..88329033df7ee --- /dev/null +++ b/src/test/compile-fail/inherit-struct3.rs @@ -0,0 +1,25 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test struct inheritance. +#![feature(struct_inherit)] + +virtual struct S1 { + f1: int, +} + +struct S6 : S1 { + f2: int, +} + +pub fn main() { + let s = S6{f2: 3}; //~ ERROR missing field: `f1` + let s = S6{f1: 3}; //~ ERROR missing field: `f2` +} diff --git a/src/test/compile-fail/inherit-struct4.rs b/src/test/compile-fail/inherit-struct4.rs new file mode 100644 index 0000000000000..e01ec2904a684 --- /dev/null +++ b/src/test/compile-fail/inherit-struct4.rs @@ -0,0 +1,24 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test struct inheritance. +#![feature(struct_inherit)] + +// With lifetime parameters. +struct S5<'a> : S4 { //~ ERROR wrong number of lifetime parameters: expected 1 but found 0 + f4: int, +} + +virtual struct S4<'a> { + f3: &'a int, +} + +pub fn main() { +} diff --git a/src/test/compile-fail/inherit-struct5.rs b/src/test/compile-fail/inherit-struct5.rs new file mode 100644 index 0000000000000..c40d27c3b6b3b --- /dev/null +++ b/src/test/compile-fail/inherit-struct5.rs @@ -0,0 +1,20 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test struct inheritance on structs from another crate. +#![feature(struct_inherit)] + +// aux-build:inherit_struct_lib.rs +extern crate inherit_struct_lib; + +struct S3 : inherit_struct_lib::S1; //~ ERROR super-struct is defined in a different crate + +pub fn main() { +} diff --git a/src/test/compile-fail/inherit-struct6.rs b/src/test/compile-fail/inherit-struct6.rs new file mode 100644 index 0000000000000..e8c86dcb31692 --- /dev/null +++ b/src/test/compile-fail/inherit-struct6.rs @@ -0,0 +1,42 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test privacy and struct inheritance. +#![feature(struct_inherit)] + +mod Foo { + pub virtual struct S1 { + pub f1: int, + f2: int, + } +} + +struct S2 : Foo::S1 { + pub f3: int, +} + +impl S2 { + fn new() -> S2 { + S2{f1: 3, f2: 4, f3: 5} //~ ERROR field `f2` of struct `S2` is private + } + + fn bar(&self) { + self.f3; + self.f1; + self.f2; //~ ERROR field `f2` of struct `S2` is private + } +} + +pub fn main() { + let s = S2{f1: 3, f2: 4, f3: 5}; //~ ERROR field `f2` of struct `S2` is private + s.f3; + s.f1; + s.f2; //~ ERROR field `f2` of struct `S2` is private +} diff --git a/src/test/compile-fail/inherit-struct7.rs b/src/test/compile-fail/inherit-struct7.rs new file mode 100644 index 0000000000000..fb0c9175d1582 --- /dev/null +++ b/src/test/compile-fail/inherit-struct7.rs @@ -0,0 +1,19 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test struct inheritance. +#![feature(struct_inherit)] + +virtual trait Foo {} //~ ERROR `virtual` keyword may only be used with `struct` +virtual enum Bar {} //~ ERROR `virtual` keyword may only be used with `struct` +virtual fn baz() {} //~ ERROR `virtual` keyword may only be used with `struct` + +pub fn main() { +} diff --git a/src/test/compile-fail/inherit-struct8.rs b/src/test/compile-fail/inherit-struct8.rs new file mode 100644 index 0000000000000..d1108349db157 --- /dev/null +++ b/src/test/compile-fail/inherit-struct8.rs @@ -0,0 +1,31 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test struct inheritance. +#![feature(struct_inherit)] + +virtual struct S1 { + f1: int, +} + +virtual struct S6 : S1 { + f2: int, +} + +struct S7 : S1 { + f1: int, //~ ERROR field `f1` hides field declared in super-struct +} + +struct S8 : S6 { + f1: int, //~ ERROR field `f1` hides field declared in super-struct +} + +pub fn main() { +} diff --git a/src/test/compile-fail/inherit-struct9.rs b/src/test/compile-fail/inherit-struct9.rs new file mode 100644 index 0000000000000..70e341d589cc7 --- /dev/null +++ b/src/test/compile-fail/inherit-struct9.rs @@ -0,0 +1,18 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test struct inheritance. +#![feature(struct_inherit)] + +struct s9; +struct s10 : s9; //~ ERROR struct inheritance is only allowed from virtual structs + +pub fn main() { +} diff --git a/src/test/debug-info/simple-struct.rs b/src/test/debug-info/simple-struct.rs index 736ab76f7dbdb..2ac3b48f6827b 100644 --- a/src/test/debug-info/simple-struct.rs +++ b/src/test/debug-info/simple-struct.rs @@ -72,15 +72,20 @@ // debugger:print 'simple-struct::PADDING_AT_END' // check:$18 = {x = -27, y = 28} -#![allow(unused_variable)] -#![allow(dead_code)] +// debugger:print inheriting +// check:$19 = {a = 10019, b = -10020, x = -10016, y = -10017.5, z = 10018} + + +#![feature(struct_inherit)]; +#![allow(unused_variable)]; +#![allow(dead_code)]; struct NoPadding16 { x: u16, y: i16 } -struct NoPadding32 { +virtual struct NoPadding32 { x: i32, y: f32, z: u32 @@ -143,6 +148,11 @@ static mut PADDING_AT_END: PaddingAtEnd = PaddingAtEnd { y: 14 }; +struct Inheriting : NoPadding32 { + a: u16, + b: i16 +} + fn main() { let no_padding16 = NoPadding16 { x: 10000, y: -10001 }; let no_padding32 = NoPadding32 { x: -10002, y: -10003.5, z: 10004 }; @@ -152,6 +162,8 @@ fn main() { let internal_padding = InternalPadding { x: 10012, y: -10013 }; let padding_at_end = PaddingAtEnd { x: -10014, y: 10015 }; + let inheriting = Inheriting { a: 10019, b: -10020, x: -10016, y: -10017.5, z: 10018 }; + unsafe { NO_PADDING_16.x = 100; NO_PADDING_16.y = -101; diff --git a/src/test/run-pass/inherit-struct1.rs b/src/test/run-pass/inherit-struct1.rs new file mode 100644 index 0000000000000..4602f13fef011 --- /dev/null +++ b/src/test/run-pass/inherit-struct1.rs @@ -0,0 +1,61 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test struct inheritance. +#![feature(struct_inherit)] + +virtual struct S1 { + f1: int, +} + +virtual struct S2 : S1 { + f2: int, +} + +struct S3 : S2 { + f3: int, +} + +// With lifetime parameters. +struct S5<'a> : S4<'a> { + f4: int, +} + +virtual struct S4<'a> { + f3: &'a int, +} + +// With type parameters. +struct S7 : S6 { + f4: int, +} + +virtual struct S6 { + f3: T, +} + +pub fn main() { + let s = S2{f1: 115, f2: 113}; + assert!(s.f1 == 115); + assert!(s.f2 == 113); + + let s = S3{f1: 15, f2: 13, f3: 17}; + assert!(s.f1 == 15); + assert!(s.f2 == 13); + assert!(s.f3 == 17); + + let s = S5{f3: &5, f4: 3}; + assert!(*s.f3 == 5); + assert!(s.f4 == 3); + + let s = S7{f3: 5u, f4: 3}; + assert!(s.f3 == 5u); + assert!(s.f4 == 3); +} diff --git a/src/test/run-pass/inherit-struct2.rs b/src/test/run-pass/inherit-struct2.rs new file mode 100644 index 0000000000000..bbcba0af68060 --- /dev/null +++ b/src/test/run-pass/inherit-struct2.rs @@ -0,0 +1,25 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test struct inheritance on structs from another crate. + +// aux-build:inherit_struct_lib.rs +extern crate inherit_struct_lib; + +pub fn main() { + let s = inherit_struct_lib::S2{f1: 115, f2: 113}; + assert!(s.f1 == 115); + assert!(s.f2 == 113); + + assert!(inherit_struct_lib::glob_s.f1 == 32); + assert!(inherit_struct_lib::glob_s.f2 == -45); + + inherit_struct_lib::test_s2(s); +}