diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 66b75d5f1cd74..cea670ba335d6 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -1299,6 +1299,38 @@ impl Resolver { let (name_bindings, new_parent) = self.add_child(ident, parent, ForbidDuplicateTypes, sp); + // If the trait has static methods, then add all the static + // methods within to a new module. + // + // We only need to create the module if the trait has static + // methods, so check that first. + let mut has_static_methods = false; + for methods.each |method| { + let ty_m = trait_method_to_ty_method(*method); + match ty_m.self_ty.node { + sty_static => { + has_static_methods = true; + break; + } + _ => {} + } + } + + // Create the module if necessary. + let module_parent_opt; + if has_static_methods { + let parent_link = self.get_parent_link(parent, ident); + name_bindings.define_module(privacy, + parent_link, + Some(local_def(item.id)), + false, + sp); + module_parent_opt = Some(ModuleReducedGraphParent( + name_bindings.get_module())); + } else { + module_parent_opt = None; + } + // Add the names of all the methods to the trait info. let method_names = @HashMap(); for methods.each |method| { @@ -1306,22 +1338,37 @@ impl Resolver { let ident = ty_m.ident; // Add it to the trait info if not static, - // add it as a name in the enclosing module otherwise. + // add it as a name in the trait module otherwise. match ty_m.self_ty.node { - sty_static => { - // which parent to use?? - let (method_name_bindings, _) = - self.add_child(ident, new_parent, - ForbidDuplicateValues, ty_m.span); - let def = def_static_method(local_def(ty_m.id), - Some(local_def(item.id)), - ty_m.purity); - (*method_name_bindings).define_value - (Public, def, ty_m.span); - } - _ => { - (*method_names).insert(ident, ()); - } + sty_static => { + let def = def_static_method( + local_def(ty_m.id), + Some(local_def(item.id)), + ty_m.purity); + + // For now, add to both the trait module and the + // enclosing module, for backwards compatibility. + let (method_name_bindings, _) = + self.add_child(ident, + new_parent, + ForbidDuplicateValues, + ty_m.span); + method_name_bindings.define_value(Public, + def, + ty_m.span); + + let (method_name_bindings, _) = + self.add_child(ident, + module_parent_opt.get(), + ForbidDuplicateValues, + ty_m.span); + method_name_bindings.define_value(Public, + def, + ty_m.span); + } + _ => { + method_names.insert(ident, ()); + } } } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index f509c15ab5f8d..ddc027f618ee9 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2275,6 +2275,8 @@ fn register_deriving_method(ccx: @crate_ctxt, } let path = vec::append(*path, ~[ + ast_map::path_mod( + ccx.sess.parse_sess.interner.intern(@fmt!("__derived%d__", id))), ast_map::path_name(derived_method_info.method_info.ident) ]); let mty = ty::lookup_item_type(ccx.tcx, local_def(id)).ty; diff --git a/src/librustc/middle/trans/deriving.rs b/src/librustc/middle/trans/deriving.rs index e2749db74aa61..284e20874dbe4 100644 --- a/src/librustc/middle/trans/deriving.rs +++ b/src/librustc/middle/trans/deriving.rs @@ -21,6 +21,38 @@ use syntax::ast_map::path; use syntax::ast_util; use syntax::ast_util::local_def; +/// The kind of deriving method this is. +enum DerivingKind { + BoolKind, // fn f(&self, other: &other) -> bool + UnitKind, // fn f(&self) -> () +} + +impl DerivingKind { + static fn of_item(ccx: @crate_ctxt, method_did: ast::def_id) + -> DerivingKind { + let item_type = ty::lookup_item_type(ccx.tcx, method_did).ty; + match ty::get(item_type).sty { + ty::ty_fn(ref f) => { + match ty::get(f.sig.output).sty { + ty::ty_bool => BoolKind, + ty::ty_nil => UnitKind, + _ => { + // XXX: Report this earlier. + ccx.tcx.sess.fatal(~"attempt to automatically derive \ + derive an implementation of a \ + function returning something \ + other than bool or ()"); + } + } + } + _ => { + ccx.tcx.sess.bug(~"DerivingKind::of_item(): method def ID \ + didn't have a function type"); + } + } + } +} + /// The main "translation" pass for automatically-derived impls. Generates /// code for monomorphic methods only. Other methods will be generated when /// they are invoked with specific type parameters; see @@ -36,15 +68,16 @@ pub fn trans_deriving_impl(ccx: @crate_ctxt, _path: path, _name: ident, impl_def_id); for method_dids.each |method_did| { + let kind = DerivingKind::of_item(ccx, *method_did); let llfn = get_item_val(ccx, method_did.node); match ty::get(self_ty.ty).sty { ty::ty_class(*) => { trans_deriving_struct_method(ccx, llfn, impl_def_id, - self_ty.ty); + self_ty.ty, kind); } ty::ty_enum(*) => { trans_deriving_enum_method(ccx, llfn, impl_def_id, - self_ty.ty); + self_ty.ty, kind); } _ => { ccx.tcx.sess.bug(~"translation of non-struct deriving \ @@ -54,8 +87,11 @@ pub fn trans_deriving_impl(ccx: @crate_ctxt, _path: path, _name: ident, } } -fn trans_deriving_struct_method(ccx: @crate_ctxt, llfn: ValueRef, - impl_did: def_id, self_ty: ty::t) { +fn trans_deriving_struct_method(ccx: @crate_ctxt, + llfn: ValueRef, + impl_did: def_id, + self_ty: ty::t, + kind: DerivingKind) { let _icx = ccx.insn_ctxt("trans_deriving_struct_method"); let fcx = new_fn_ctxt(ccx, ~[], llfn, None); let top_bcx = top_scope_block(fcx, None); @@ -64,7 +100,13 @@ fn trans_deriving_struct_method(ccx: @crate_ctxt, llfn: ValueRef, let llselfty = type_of(ccx, self_ty); let llselfval = PointerCast(bcx, fcx.llenv, T_ptr(llselfty)); - let llotherval = llvm::LLVMGetParam(llfn, 2); + + // If there is an "other" value, then get it. + let llotherval_opt; + match kind { + BoolKind => llotherval_opt = Some(llvm::LLVMGetParam(llfn, 2)), + UnitKind => llotherval_opt = None + } let struct_field_tys; match ty::get(self_ty).sty { @@ -82,27 +124,43 @@ fn trans_deriving_struct_method(ccx: @crate_ctxt, llfn: ValueRef, for ccx.tcx.deriving_struct_methods.get(impl_did).eachi |i, derived_method_info| { let llselfval = GEPi(bcx, llselfval, [0, 0, i]); - let llotherval = GEPi(bcx, llotherval, [0, 0, i]); + + let llotherval_opt = llotherval_opt.map( + |llotherval| GEPi(bcx, *llotherval, [0, 0, i])); let self_ty = struct_field_tys[i].mt.ty; bcx = call_substructure_method(bcx, derived_method_info, self_ty, - llselfval, llotherval); + llselfval, llotherval_opt); + + // If this derived method is of boolean kind, return immediately if + // the call to the substructure method returned false. + match kind { + BoolKind => { + let next_block = sub_block(top_bcx, ~"next"); + let llcond = Load(bcx, fcx.llretptr); + CondBr(bcx, llcond, next_block.llbb, fcx.llreturn); + bcx = next_block; + } + UnitKind => {} // Unconditionally continue. + } + } - // Return immediately if the call returned false. - let next_block = sub_block(top_bcx, ~"next"); - let llcond = Load(bcx, fcx.llretptr); - CondBr(bcx, llcond, next_block.llbb, fcx.llreturn); - bcx = next_block; + // Store true if necessary. + match kind { + BoolKind => Store(bcx, C_bool(true), fcx.llretptr), + UnitKind => {} } - Store(bcx, C_bool(true), fcx.llretptr); Br(bcx, fcx.llreturn); finish_fn(fcx, lltop); } -fn trans_deriving_enum_method(ccx: @crate_ctxt, llfn: ValueRef, - impl_did: def_id, self_ty: ty::t) { +fn trans_deriving_enum_method(ccx: @crate_ctxt, + llfn: ValueRef, + impl_did: def_id, + self_ty: ty::t, + kind: DerivingKind) { let _icx = ccx.insn_ctxt("trans_deriving_enum_method"); let fcx = new_fn_ctxt(ccx, ~[], llfn, None); let top_bcx = top_scope_block(fcx, None); @@ -111,7 +169,12 @@ fn trans_deriving_enum_method(ccx: @crate_ctxt, llfn: ValueRef, let llselfty = type_of(ccx, self_ty); let llselfval = PointerCast(bcx, fcx.llenv, T_ptr(llselfty)); - let llotherval = llvm::LLVMGetParam(llfn, 2); + + let llotherval_opt; + match kind { + UnitKind => llotherval_opt = None, + BoolKind => llotherval_opt = Some(llvm::LLVMGetParam(llfn, 2)) + } let enum_id, enum_substs, enum_variant_infos; match ty::get(self_ty).sty { @@ -127,11 +190,18 @@ fn trans_deriving_enum_method(ccx: @crate_ctxt, llfn: ValueRef, } } - // Create the "no match" basic block. This is a basic block that does - // nothing more than return false. - let nomatch_bcx = sub_block(top_bcx, ~"no_match"); - Store(nomatch_bcx, C_bool(false), fcx.llretptr); - Br(nomatch_bcx, fcx.llreturn); + // Create the "no match" basic block, if necessary. This is a basic block + // that does nothing more than return false. + let nomatch_bcx_opt; + match kind { + BoolKind => { + let nomatch_bcx = sub_block(top_bcx, ~"no_match"); + Store(nomatch_bcx, C_bool(false), fcx.llretptr); + Br(nomatch_bcx, fcx.llreturn); + nomatch_bcx_opt = Some(nomatch_bcx); + } + UnitKind => nomatch_bcx_opt = None + } // Create the "unreachable" basic block. let unreachable_bcx = sub_block(top_bcx, ~"unreachable"); @@ -144,11 +214,13 @@ fn trans_deriving_enum_method(ccx: @crate_ctxt, llfn: ValueRef, if n_variants != 1 { // Grab the two discriminants. let llselfdiscrim = Load(bcx, GEPi(bcx, llselfval, [0, 0])); - let llotherdiscrim = Load(bcx, GEPi(bcx, llotherval, [0, 0])); + let llotherdiscrim_opt = llotherval_opt.map( + |llotherval| Load(bcx, GEPi(bcx, *llotherval, [0, 0]))); // Skip over the discriminants and compute the address of the payload. let llselfpayload = GEPi(bcx, llselfval, [0, 1]); - let llotherpayload = GEPi(bcx, llotherval, [0, 1]); + let llotherpayload_opt = llotherval_opt.map( + |llotherval| GEPi(bcx, *llotherval, [0, 1])); // Create basic blocks for the outer switch. let outer_bcxs = vec::from_fn( @@ -169,46 +241,71 @@ fn trans_deriving_enum_method(ccx: @crate_ctxt, llfn: ValueRef, enum_variant_infos[self_variant_index].id; let llselfval = GEP_enum(match_bcx, llselfpayload, enum_id, variant_def_id, enum_substs.tps, i); - let llotherval = GEP_enum(match_bcx, llotherpayload, - enum_id, variant_def_id, - enum_substs.tps, i); + + let llotherval_opt = llotherpayload_opt.map(|llotherpayload| + GEP_enum(match_bcx, *llotherpayload, enum_id, + variant_def_id, enum_substs.tps, i)); let self_ty = enum_variant_infos[self_variant_index].args[i]; match_bcx = call_substructure_method(match_bcx, derived_method_info, self_ty, llselfval, - llotherval); - - // Return immediately if the call to the substructure returned - // false. - let next_bcx = sub_block( - top_bcx, fmt!("next_%u_%u", self_variant_index, i)); - let llcond = Load(match_bcx, fcx.llretptr); - CondBr(match_bcx, llcond, next_bcx.llbb, fcx.llreturn); - match_bcx = next_bcx; + llotherval_opt); + + // If this is a boolean-kind deriving method, then return + // immediately if the call to the substructure returned false. + match kind { + BoolKind => { + let next_bcx = sub_block(top_bcx, + fmt!("next_%u_%u", + self_variant_index, + i)); + let llcond = Load(match_bcx, fcx.llretptr); + CondBr(match_bcx, + llcond, + next_bcx.llbb, + fcx.llreturn); + match_bcx = next_bcx; + } + UnitKind => {} + } + } + + // Store true in the return pointer if this is a boolean-kind + // deriving method. + match kind { + BoolKind => Store(match_bcx, C_bool(true), fcx.llretptr), + UnitKind => {} } // Finish up the matching block. - Store(match_bcx, C_bool(true), fcx.llretptr); Br(match_bcx, fcx.llreturn); - // Build the inner switch. - let llswitch = Switch( - *bcx, llotherdiscrim, unreachable_bcx.llbb, n_variants); - for uint::range(0, n_variants) |other_variant_index| { - let discriminant = - enum_variant_infos[other_variant_index].disr_val; - if self_variant_index == other_variant_index { - // This is the potentially-matching case. - AddCase(llswitch, - C_int(ccx, discriminant), - top_match_bcx.llbb); - } else { - // This is always a non-matching case. - AddCase(llswitch, - C_int(ccx, discriminant), - nomatch_bcx.llbb); + // If this is a boolean-kind derived method, build the inner + // switch. Otherwise, just jump to the matching case. + match llotherdiscrim_opt { + None => Br(*bcx, top_match_bcx.llbb), + Some(copy llotherdiscrim) => { + let llswitch = Switch(*bcx, + llotherdiscrim, + unreachable_bcx.llbb, + n_variants); + for uint::range(0, n_variants) |other_variant_index| { + let discriminant = + enum_variant_infos[other_variant_index].disr_val; + if self_variant_index == other_variant_index { + // This is the potentially-matching case. + AddCase(llswitch, + C_int(ccx, discriminant), + top_match_bcx.llbb); + } else { + // This is always a non-matching case. + AddCase(llswitch, + C_int(ccx, discriminant), + nomatch_bcx_opt.get().llbb); + } + } } } } @@ -233,7 +330,7 @@ fn call_substructure_method(bcx: block, derived_field_info: &DerivedFieldInfo, self_ty: ty::t, llselfval: ValueRef, - llotherval: ValueRef) -> block { + llotherval_opt: Option) -> block { let fcx = bcx.fcx; let ccx = fcx.ccx; @@ -273,12 +370,18 @@ fn call_substructure_method(bcx: block, } }; + let arg_values; + match llotherval_opt { + None => arg_values = ArgVals(~[]), + Some(copy llotherval) => arg_values = ArgVals(~[llotherval]) + } + callee::trans_call_inner(bcx, None, fn_expr_tpbt.ty, ty::mk_bool(ccx.tcx), cb, - ArgVals(~[llotherval]), + move arg_values, SaveIn(fcx.llretptr), DontAutorefArg) } diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs index 5ad3b27e619ed..7a863b0cc39bf 100644 --- a/src/librustc/middle/typeck/coherence.rs +++ b/src/librustc/middle/typeck/coherence.rs @@ -262,6 +262,9 @@ impl CoherenceChecker { } } + // We only want to generate one Impl structure. When we generate one, + // we store it here so that we don't recreate it. + let mut implementation_opt = None; for associated_traits.each |associated_trait| { let trait_did = self.trait_ref_to_trait_def_id(*associated_trait); @@ -273,8 +276,14 @@ impl CoherenceChecker { self.crate_context.tcx.sess.str_of(item.ident)); self.instantiate_default_methods(item.id, trait_did); - let implementation = self.create_impl_from_item(item); - self.add_trait_method(trait_did, implementation); + + let implementation; + if implementation_opt.is_none() { + implementation = self.create_impl_from_item(item); + implementation_opt = Some(implementation); + } + + self.add_trait_method(trait_did, implementation_opt.get()); } // Add the implementation to the mapping from implementation to base @@ -288,7 +297,15 @@ impl CoherenceChecker { } Some(base_type_def_id) => { // XXX: Gather up default methods? - let implementation = self.create_impl_from_item(item); + let implementation; + match implementation_opt { + None => { + implementation = self.create_impl_from_item(item); + } + Some(copy existing_implementation) => { + implementation = existing_implementation; + } + } self.add_inherent_method(base_type_def_id, implementation); self.base_type_def_ids.insert(local_def(item.id), @@ -620,6 +637,7 @@ impl CoherenceChecker { method_info: method_info, containing_impl: impl_did }; + tcx.automatically_derived_methods.insert(new_def_id, derived_method_info); @@ -666,18 +684,21 @@ impl CoherenceChecker { } } None => { - // This is a "deriving" impl. For each trait, collect - // all the "required" methods and add them to the - // Impl structure. + // This is a "deriving" impl. For each trait, + // collect all the "required" methods and add + // them to the Impl structure. let tcx = self.crate_context.tcx; - let self_ty = - ty::lookup_item_type(tcx, local_def(item.id)); + let self_ty = ty::lookup_item_type( + tcx, local_def(item.id)); for trait_refs.each |trait_ref| { let trait_did = - self.trait_ref_to_trait_def_id(*trait_ref); + self.trait_ref_to_trait_def_id( + *trait_ref); self.add_automatically_derived_methods_from_trait( - &mut methods, trait_did, self_ty.ty, - local_def(item.id)); + &mut methods, + trait_did, + self_ty.ty, + local_def(item.id)); } } } diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs index 38ccb64899ca9..c2e255cad149e 100644 --- a/src/libsyntax/ast_map.rs +++ b/src/libsyntax/ast_map.rs @@ -207,8 +207,7 @@ fn map_item(i: @item, cx: ctx, v: vt) { let impl_did = ast_util::local_def(i.id); for ms_opt.each |ms| { for ms.each |m| { - map_method(impl_did, extend(cx, i.ident), *m, - cx); + map_method(impl_did, extend(cx, i.ident), *m, cx); } } } diff --git a/src/test/run-pass/deriving-returning-nil.rs b/src/test/run-pass/deriving-returning-nil.rs new file mode 100644 index 0000000000000..ed399b998cea8 --- /dev/null +++ b/src/test/run-pass/deriving-returning-nil.rs @@ -0,0 +1,40 @@ +trait Show { + fn show(); +} + +impl int : Show { + fn show() { + io::println(self.to_str()); + } +} + +struct Foo { + x: int, + y: int, + z: int, +} + +impl Foo : Show; + +enum Bar { + Baz(int, int), + Boo(Foo), +} + +impl Bar : Show; + +fn main() { + let foo = Foo { x: 1, y: 2, z: 3 }; + foo.show(); + + io::println("---"); + + let baz = Baz(4, 5); + baz.show(); + + io::println("---"); + + let boo = Boo(Foo { x: 6, y: 7, z: 8 }); + boo.show(); +} + diff --git a/src/test/run-pass/static-methods-in-traits.rs b/src/test/run-pass/static-methods-in-traits.rs new file mode 100644 index 0000000000000..717a720ad8150 --- /dev/null +++ b/src/test/run-pass/static-methods-in-traits.rs @@ -0,0 +1,25 @@ +mod a { + pub trait Foo { + static pub fn foo() -> self; + } + + impl int : Foo { + static pub fn foo() -> int { + 3 + } + } + + impl uint : Foo { + static pub fn foo() -> uint { + 5u + } + } +} + +fn main() { + let x: int = a::Foo::foo(); + let y: uint = a::Foo::foo(); + assert x == 3; + assert y == 5; +} +