From e872e3673442d200aa9927f0759ed3c0ae977509 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 2 Nov 2012 13:33:51 -0700 Subject: [PATCH 1/2] rustc: Implement parsing and typechecking for "once fn" --- src/libsyntax/ast.rs | 21 +++++++++++- src/libsyntax/fold.rs | 9 ++--- src/libsyntax/parse/parser.rs | 39 +++++++++++++++++---- src/libsyntax/parse/token.rs | 1 + src/libsyntax/print/pprust.rs | 36 ++++++++++++++----- src/libsyntax/visit.rs | 2 +- src/rustc/metadata/tydecode.rs | 10 ++++++ src/rustc/metadata/tyencode.rs | 8 +++++ src/rustc/middle/lint.rs | 6 ++-- src/rustc/middle/region.rs | 10 +++--- src/rustc/middle/trans/foreign.rs | 1 + src/rustc/middle/trans/monomorphize.rs | 2 ++ src/rustc/middle/ty.rs | 11 ++++++ src/rustc/middle/typeck/astconv.rs | 19 +++++++--- src/rustc/middle/typeck/check.rs | 29 ++++++++++++---- src/rustc/middle/typeck/collect.rs | 40 +++++++++++++++++----- src/rustc/middle/typeck/infer/combine.rs | 14 +++++--- src/rustc/middle/typeck/infer/glb.rs | 8 +++++ src/rustc/middle/typeck/infer/lub.rs | 8 +++++ src/rustc/middle/typeck/infer/sub.rs | 6 ++++ src/rustc/util/ppaux.rs | 40 +++++++++++++++++----- src/test/compile-fail/once-fn-subtyping.rs | 5 +++ 22 files changed, 264 insertions(+), 61 deletions(-) create mode 100644 src/test/compile-fail/once-fn-subtyping.rs diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index a3e57716d173f..0d619aadad4d3 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1091,6 +1091,25 @@ enum region_ { re_named(ident) } +#[auto_serialize] +#[auto_deserialize] +enum onceness { + on_once, + on_many +} + +impl onceness : cmp::Eq { + pure fn eq(other: &onceness) -> bool { + match (self, *other) { + (on_once, on_once) | (on_many, on_many) => true, + _ => false + } + } + pure fn ne(other: &onceness) -> bool { + !self.eq(other) + } +} + #[auto_serialize] #[auto_deserialize] enum ty_ { @@ -1102,7 +1121,7 @@ enum ty_ { ty_ptr(mt), ty_rptr(@region, mt), ty_rec(~[ty_field]), - ty_fn(proto, purity, @~[ty_param_bound], fn_decl), + ty_fn(proto, purity, onceness, @~[ty_param_bound], fn_decl), ty_tup(~[@Ty]), ty_path(@path, node_id), ty_fixed_length(@Ty, Option), diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 8efe4c9944ca1..68dcf7d4e70f5 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -518,10 +518,11 @@ fn noop_fold_ty(t: ty_, fld: ast_fold) -> ty_ { ty_ptr(mt) => ty_ptr(fold_mt(mt, fld)), ty_rptr(region, mt) => ty_rptr(region, fold_mt(mt, fld)), ty_rec(fields) => ty_rec(vec::map(fields, |f| fold_field(*f, fld))), - ty_fn(proto, purity, bounds, decl) => - ty_fn(proto, purity, - @vec::map(*bounds, - |x| fold_ty_param_bound(*x, fld)), + ty_fn(proto, purity, onceness, bounds, decl) => + ty_fn(proto, + purity, + onceness, + @vec::map(*bounds, |x| fold_ty_param_bound(*x, fld)), fold_fn_decl(decl, fld)), ty_tup(tys) => ty_tup(vec::map(tys, |ty| fld.fold_ty(*ty))), ty_path(path, id) => ty_path(fld.fold_path(path), fld.new_id(id)), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 6615bc751694b..eb430ee0ad905 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -287,7 +287,7 @@ impl Parser { pure fn id_to_str(id: ident) -> @~str { self.sess.interner.get(id) } - fn parse_ty_fn(purity: ast::purity) -> ty_ { + fn parse_ty_fn(purity: ast::purity, onceness: ast::onceness) -> ty_ { let proto, bounds; if self.eat_keyword(~"extern") { self.expect_keyword(~"fn"); @@ -298,7 +298,17 @@ impl Parser { proto = self.parse_fn_ty_proto(); bounds = self.parse_optional_ty_param_bounds(); }; - ty_fn(proto, purity, bounds, self.parse_ty_fn_decl()) + ty_fn(proto, purity, onceness, bounds, self.parse_ty_fn_decl()) + } + + fn parse_ty_fn_with_onceness(purity: ast::purity) -> ty_ { + let onceness = self.parse_optional_onceness(); + self.parse_ty_fn(purity, onceness) + } + + fn parse_ty_fn_with_purity_and_onceness() -> ty_ { + let purity = self.parse_optional_purity(); + self.parse_ty_fn_with_onceness(purity) } fn parse_ty_fn_decl() -> fn_decl { @@ -526,15 +536,18 @@ impl Parser { let region = self.parse_region_with_sep(); let mt = self.parse_mt(); ty_rptr(region, mt) + } else if self.eat_keyword(~"once") { + self.parse_ty_fn(ast::impure_fn, ast::on_once) } else if self.eat_keyword(~"pure") { - self.parse_ty_fn(ast::pure_fn) + self.parse_ty_fn_with_onceness(ast::pure_fn) } else if self.eat_keyword(~"unsafe") { - self.parse_ty_fn(ast::unsafe_fn) + self.parse_ty_fn_with_onceness(ast::unsafe_fn) } else if self.is_keyword(~"fn") { - self.parse_ty_fn(ast::impure_fn) + self.parse_ty_fn_with_onceness(ast::impure_fn) } else if self.eat_keyword(~"extern") { self.expect_keyword(~"fn"); - ty_fn(proto_bare, ast::impure_fn, @~[], self.parse_ty_fn_decl()) + ty_fn(proto_bare, ast::impure_fn, ast::on_many, @~[], + self.parse_ty_fn_decl()) } else if self.token == token::MOD_SEP || is_ident(self.token) { let path = self.parse_path_with_tps(colons_before_params); ty_path(path, self.get_id()) @@ -2275,6 +2288,20 @@ impl Parser { self.get_id()), span: self.last_span} } + fn parse_optional_purity() -> ast::purity { + if self.eat_keyword(~"pure") { + ast::pure_fn + } else if self.eat_keyword(~"unsafe") { + ast::unsafe_fn + } else { + ast::impure_fn + } + } + + fn parse_optional_onceness() -> ast::onceness { + if self.eat_keyword(~"once") { ast::on_once } else { ast::on_many } + } + fn parse_optional_ty_param_bounds() -> @~[ty_param_bound] { let mut bounds = ~[]; if self.eat(token::COLON) { diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 53c1ce1c7f535..0d139b101d8f3 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -427,6 +427,7 @@ fn strict_keyword_table() -> HashMap<~str, ()> { ~"if", ~"impl", ~"let", ~"log", ~"loop", ~"match", ~"mod", ~"move", ~"mut", + ~"once", ~"priv", ~"pub", ~"pure", ~"ref", ~"return", ~"struct", diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 3836c21ff2449..4e2bec769c594 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -394,8 +394,9 @@ fn print_type_ex(s: ps, &&ty: @ast::Ty, print_colons: bool) { commasep(s, inconsistent, elts, print_type); pclose(s); } - ast::ty_fn(proto, purity, bounds, d) => { - print_ty_fn(s, Some(proto), purity, bounds, d, None, None, None); + ast::ty_fn(proto, purity, onceness, bounds, d) => { + print_ty_fn(s, Some(proto), purity, onceness, bounds, d, None, None, + None); } ast::ty_path(path, _) => print_path(s, path, print_colons), ast::ty_fixed_length(t, v) => { @@ -804,7 +805,7 @@ fn print_ty_method(s: ps, m: ast::ty_method) { hardbreak_if_not_bol(s); maybe_print_comment(s, m.span.lo); print_outer_attributes(s, m.attrs); - print_ty_fn(s, None, m.purity, + print_ty_fn(s, None, m.purity, ast::on_many, @~[], m.decl, Some(m.ident), Some(m.tps), Some(m.self_ty.node)); word(s.s, ~";"); @@ -1273,7 +1274,7 @@ fn print_expr(s: ps, &&expr: @ast::expr) { cbox(s, indent_unit); // head-box, will be closed by print-block at start ibox(s, 0u); - word(s.s, fn_header_info_to_str(None, None, Some(proto), + word(s.s, fn_header_info_to_str(None, None, Some(proto), ast::on_many, ast::inherited)); print_fn_args_and_ret(s, decl, *cap_clause, None); space(s.s); @@ -1606,12 +1607,15 @@ fn print_self_ty(s: ps, self_ty: ast::self_ty_) -> bool { return true; } -fn print_fn(s: ps, decl: ast::fn_decl, purity: Option, +fn print_fn(s: ps, + decl: ast::fn_decl, + purity: Option, name: ast::ident, typarams: ~[ast::ty_param], opt_self_ty: Option, vis: ast::visibility) { - head(s, fn_header_info_to_str(opt_self_ty, purity, None, vis)); + head(s, fn_header_info_to_str(opt_self_ty, purity, None, ast::on_many, + vis)); print_ident(s, name); print_type_params(s, typarams); print_fn_args_and_ret(s, decl, ~[], opt_self_ty); @@ -1831,14 +1835,17 @@ fn print_arg(s: ps, input: ast::arg) { end(s); } -fn print_ty_fn(s: ps, opt_proto: Option, purity: ast::purity, +fn print_ty_fn(s: ps, + opt_proto: Option, + purity: ast::purity, + onceness: ast::onceness, bounds: @~[ast::ty_param_bound], decl: ast::fn_decl, id: Option, tps: Option<~[ast::ty_param]>, opt_self_ty: Option) { ibox(s, indent_unit); word(s.s, fn_header_info_to_str(opt_self_ty, Some(purity), opt_proto, - ast::inherited)); + onceness, ast::inherited)); print_bounds(s, bounds); match id { Some(id) => { word(s.s, ~" "); print_ident(s, id); } _ => () } match tps { Some(tps) => print_type_params(s, tps), _ => () } @@ -2062,6 +2069,7 @@ fn next_comment(s: ps) -> Option { fn fn_header_info_to_str(opt_sty: Option, opt_purity: Option, opt_p: Option, + onceness: ast::onceness, vis: ast::visibility) -> ~str { let mut s = visibility_qualified(vis, ~""); @@ -2082,6 +2090,11 @@ fn fn_header_info_to_str(opt_sty: Option, str::push_str(&mut s, opt_proto_to_str(opt_p)); + match onceness { + ast::on_once => str::push_str(&mut s, ~"once "), + ast::on_many => {} + } + return s; } @@ -2101,6 +2114,13 @@ pure fn purity_to_str(p: ast::purity) -> ~str { } } +pure fn onceness_to_str(o: ast::onceness) -> ~str { + match o { + ast::on_once => ~"once", + ast::on_many => ~"many" + } +} + fn print_purity(s: ps, p: ast::purity) { match p { ast::impure_fn => (), diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 2dbe2b16044ef..be6fb4cefa83e 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -203,7 +203,7 @@ fn visit_ty(t: @Ty, e: E, v: vt) { ty_tup(ts) => for ts.each |tt| { v.visit_ty(*tt, e, v); }, - ty_fn(_, _, bounds, decl) => { + ty_fn(_, _, _, bounds, decl) => { for decl.inputs.each |a| { v.visit_ty(a.ty, e, v); } visit_ty_param_bounds(bounds, e, v); v.visit_ty(decl.output, e, v); diff --git a/src/rustc/metadata/tydecode.rs b/src/rustc/metadata/tydecode.rs index 14aef6db1adbd..ec83fed8b600b 100644 --- a/src/rustc/metadata/tydecode.rs +++ b/src/rustc/metadata/tydecode.rs @@ -387,6 +387,14 @@ fn parse_purity(c: char) -> purity { } } +fn parse_onceness(c: char) -> ast::onceness { + match c { + 'o' => ast::on_once, + 'm' => ast::on_many, + _ => fail ~"parse_onceness: bad onceness" + } +} + fn parse_arg(st: @pstate, conv: conv_did) -> ty::arg { {mode: parse_mode(st), ty: parse_ty(st, conv)} @@ -406,6 +414,7 @@ fn parse_mode(st: @pstate) -> ast::mode { fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::FnTy { let proto = parse_proto(st); let purity = parse_purity(next(st)); + let onceness = parse_onceness(next(st)); let bounds = parse_bounds(st, conv); assert (next(st) == '['); let mut inputs: ~[ty::arg] = ~[]; @@ -418,6 +427,7 @@ fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::FnTy { return FnTyBase { meta: FnMeta {purity: purity, proto: proto, + onceness: onceness, bounds: bounds, ret_style: ret_style}, sig: FnSig {inputs: inputs, diff --git a/src/rustc/metadata/tyencode.rs b/src/rustc/metadata/tyencode.rs index e69815379737c..08c26fdfb6d98 100644 --- a/src/rustc/metadata/tyencode.rs +++ b/src/rustc/metadata/tyencode.rs @@ -349,9 +349,17 @@ fn enc_purity(w: io::Writer, p: purity) { } } +fn enc_onceness(w: io::Writer, o: onceness) { + match o { + on_once => w.write_char('o'), + on_many => w.write_char('m') + } +} + fn enc_ty_fn(w: io::Writer, cx: @ctxt, ft: ty::FnTy) { enc_proto(w, cx, ft.meta.proto); enc_purity(w, ft.meta.purity); + enc_onceness(w, ft.meta.onceness); enc_bounds(w, cx, ft.meta.bounds); w.write_char('['); for ft.sig.inputs.each |arg| { diff --git a/src/rustc/middle/lint.rs b/src/rustc/middle/lint.rs index 192b2924cb14d..9d28e35acb0de 100644 --- a/src/rustc/middle/lint.rs +++ b/src/rustc/middle/lint.rs @@ -841,7 +841,7 @@ fn check_fn_deprecated_modes(tcx: ty::ctxt, fn_ty: ty::t, decl: ast::fn_decl, let span = arg_ast.ty.span; // Recurse to check fn-type argument match arg_ast.ty.node { - ast::ty_fn(_, _, _, decl) => { + ast::ty_fn(_, _, _, _, decl) => { check_fn_deprecated_modes(tcx, arg_ty.ty, decl, span, id); } @@ -856,7 +856,7 @@ fn check_fn_deprecated_modes(tcx: ty::ctxt, fn_ty: ty::t, decl: ast::fn_decl, // Functions with preceding sigil are parsed // as pointers of functions match mt.ty.node { - ast::ty_fn(_, _, _, decl) => { + ast::ty_fn(_, _, _, _, decl) => { check_fn_deprecated_modes( tcx, arg_ty.ty, decl, span, id); @@ -889,7 +889,7 @@ fn check_item_deprecated_modes(tcx: ty::ctxt, it: @ast::item) { match it.node { ast::item_ty(ty, _) => { match ty.node { - ast::ty_fn(_, _, _, decl) => { + ast::ty_fn(_, _, _, _, decl) => { let fn_ty = ty::node_id_to_type(tcx, it.id); check_fn_deprecated_modes( tcx, fn_ty, decl, ty.span, it.id) diff --git a/src/rustc/middle/region.rs b/src/rustc/middle/region.rs index 415cd12536c05..a98ec2181d923 100644 --- a/src/rustc/middle/region.rs +++ b/src/rustc/middle/region.rs @@ -624,8 +624,8 @@ fn determine_rp_in_ty(ty: @ast::Ty, } } - ast::ty_fn(ast::proto_bare, _, _, _) | - ast::ty_fn(ast::proto_block, _, _, _) if cx.anon_implies_rp => { + ast::ty_fn(ast::proto_bare, _, _, _, _) | + ast::ty_fn(ast::proto_block, _, _, _, _) if cx.anon_implies_rp => { debug!("referenced bare fn type with regions %s", pprust::ty_to_str(ty, cx.sess.intr())); cx.add_rp(cx.item_id, cx.add_variance(rv_contravariant)); @@ -672,8 +672,8 @@ fn determine_rp_in_ty(ty: @ast::Ty, match ty.node { ast::ty_box(mt) | ast::ty_uniq(mt) => { match mt.ty.node { - ast::ty_fn(ast::proto_bare, _, _, _) | - ast::ty_fn(ast::proto_block, _, _, _) => { + ast::ty_fn(ast::proto_bare, _, _, _, _) | + ast::ty_fn(ast::proto_block, _, _, _, _) => { do cx.with(cx.item_id, false) { visit_mt(mt, cx, visitor); } @@ -706,7 +706,7 @@ fn determine_rp_in_ty(ty: @ast::Ty, } } - ast::ty_fn(_, _, bounds, decl) => { + ast::ty_fn(_, _, _, bounds, decl) => { // fn() binds the & region, so do not consider &T types that // appear *inside* a fn() type to affect the enclosing item: do cx.with(cx.item_id, false) { diff --git a/src/rustc/middle/trans/foreign.rs b/src/rustc/middle/trans/foreign.rs index 8fa23ef8fabd8..09a23beb43a6f 100644 --- a/src/rustc/middle/trans/foreign.rs +++ b/src/rustc/middle/trans/foreign.rs @@ -1000,6 +1000,7 @@ fn trans_intrinsic(ccx: @crate_ctxt, decl: ValueRef, item: @ast::foreign_item, proto: ty::proto_vstore(ty::vstore_slice( ty::re_bound(ty::br_anon(0)))), + onceness: ast::on_many, bounds: @~[], ret_style: ast::return_val}, sig: FnSig {inputs: ~[{mode: ast::expl(ast::by_val), diff --git a/src/rustc/middle/trans/monomorphize.rs b/src/rustc/middle/trans/monomorphize.rs index 4a569e2cf4de4..0bd520e2cc2cf 100644 --- a/src/rustc/middle/trans/monomorphize.rs +++ b/src/rustc/middle/trans/monomorphize.rs @@ -250,6 +250,7 @@ fn normalize_for_monomorphization(tcx: ty::ctxt, ty: ty::t) -> Option { tcx, FnTyBase {meta: FnMeta {purity: ast::impure_fn, proto: fty.meta.proto, + onceness: ast::on_many, bounds: @~[], ret_style: ast::return_val}, sig: FnSig {inputs: ~[], @@ -261,6 +262,7 @@ fn normalize_for_monomorphization(tcx: ty::ctxt, ty: ty::t) -> Option { tcx, FnTyBase {meta: FnMeta {purity: ast::impure_fn, proto: box_proto, + onceness: ast::on_many, bounds: @~[], ret_style: ast::return_val}, sig: FnSig {inputs: ~[], diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 4391388331c63..848f8a3a1dd06 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -126,6 +126,7 @@ export kind_is_owned; export meta_kind, kind_lteq, type_kind; export operators; export type_err, terr_vstore_kind; +export terr_onceness_mismatch; export type_err_to_str, note_and_explain_type_err; export expected_found; export type_needs_drop; @@ -186,6 +187,7 @@ export terr_proto_mismatch; export terr_ret_style_mismatch; export terr_fn, terr_trait; export purity_to_str; +export onceness_to_str; export param_tys_in_type; export eval_repeat_count; export fn_proto, proto_bare, proto_vstore; @@ -504,11 +506,14 @@ impl fn_proto : cmp::Eq { * * - `purity` is the function's effect (pure, impure, unsafe). * - `proto` is the protocol (fn@, fn~, etc). + * - `onceness` indicates whether the function can be called one time or many + * times. * - `bounds` is the parameter bounds on the function's upvars. * - `ret_style` indicates whether the function returns a value or fails. */ struct FnMeta { purity: ast::purity, proto: fn_proto, + onceness: ast::onceness, bounds: @~[param_bound], ret_style: ret_style } @@ -679,6 +684,7 @@ enum type_err { terr_mismatch, terr_ret_style_mismatch(expected_found), terr_purity_mismatch(expected_found), + terr_onceness_mismatch(expected_found), terr_mutability, terr_proto_mismatch(expected_found), terr_box_mutability, @@ -3326,6 +3332,11 @@ fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str { purity_to_str(values.expected), purity_to_str(values.found)) } + terr_onceness_mismatch(values) => { + fmt!("expected %s fn but found %s fn", + onceness_to_str(values.expected), + onceness_to_str(values.found)) + } terr_proto_mismatch(values) => { fmt!("expected %s closure, found %s closure", proto_ty_to_str(cx, values.expected), diff --git a/src/rustc/middle/typeck/astconv.rs b/src/rustc/middle/typeck/astconv.rs index d793955e447dc..ec9365ed5b6e4 100644 --- a/src/rustc/middle/typeck/astconv.rs +++ b/src/rustc/middle/typeck/astconv.rs @@ -208,7 +208,8 @@ fn ast_ty_to_ty( _ => () } } - ast::ty_fn(ast::proto_block, purity, ast_bounds, ast_fn_decl) => { + ast::ty_fn(ast::proto_block, purity, onceness, ast_bounds, + ast_fn_decl) => { let new_proto; match vst { ty::vstore_fixed(_) => { @@ -223,9 +224,15 @@ fn ast_ty_to_ty( // Run through the normal function type conversion process. let bounds = collect::compute_bounds(self.ccx(), ast_bounds); - let fn_decl = ty_of_fn_decl(self, rscope, new_proto, purity, + let fn_decl = ty_of_fn_decl(self, + rscope, + new_proto, + purity, + onceness, bounds, - ast_fn_decl, None, span); + ast_fn_decl, + None, + span); return ty::mk_fn(tcx, fn_decl); } _ => () @@ -309,10 +316,10 @@ fn ast_ty_to_ty( }; ty::mk_rec(tcx, flds) } - ast::ty_fn(proto, purity, ast_bounds, decl) => { + ast::ty_fn(proto, purity, onceness, ast_bounds, decl) => { let bounds = collect::compute_bounds(self.ccx(), ast_bounds); let fn_decl = ty_of_fn_decl(self, rscope, proto, purity, - bounds, decl, None, + onceness, bounds, decl, None, ast_ty.span); ty::mk_fn(tcx, fn_decl) } @@ -476,6 +483,7 @@ fn ty_of_fn_decl( self: AC, rscope: RS, ast_proto: ast::proto, purity: ast::purity, + onceness: ast::onceness, bounds: @~[ty::param_bound], decl: ast::fn_decl, expected_tys: expected_tys, @@ -508,6 +516,7 @@ fn ty_of_fn_decl( FnTyBase { meta: FnMeta {purity: purity, proto: proto, + onceness: onceness, bounds: bounds, ret_style: decl.cf}, sig: FnSig {inputs: input_tys, diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs index ae123f8dc5a0c..720b62152e6ff 100644 --- a/src/rustc/middle/typeck/check.rs +++ b/src/rustc/middle/typeck/check.rs @@ -1310,7 +1310,10 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, // block syntax lambdas; that is, lambdas without explicit // protos. let expected_sty = unpack_expected(fcx, expected, |x| Some(x)); - let (expected_tys, expected_purity, expected_proto) = + let (expected_tys, + expected_purity, + expected_proto, + expected_onceness) = match expected_sty { Some(ty::ty_fn(ref fn_ty)) => { let {fn_ty, _} = @@ -1320,10 +1323,14 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, (Some({inputs: fn_ty.sig.inputs, output: fn_ty.sig.output}), fn_ty.meta.purity, - fn_ty.meta.proto) + fn_ty.meta.proto, + fn_ty.meta.onceness) } _ => { - (None, ast::impure_fn, ty::proto_vstore(ty::vstore_box)) + (None, + ast::impure_fn, + ty::proto_vstore(ty::vstore_box), + ast::on_many) } }; @@ -1334,17 +1341,25 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, // XXX: This is a hack. let ast_proto = ast_proto_opt.get_default(ast::proto_box); let ast_purity = ast::impure_fn; + let ast_onceness = ast::on_many; // construct the function type - let mut fn_ty = astconv::ty_of_fn_decl(fcx, fcx, - ast_proto, ast_purity, @~[], - decl, expected_tys, expr.span); + let mut fn_ty = astconv::ty_of_fn_decl(fcx, + fcx, + ast_proto, + ast_purity, + ast_onceness, + @~[], + decl, + expected_tys, + expr.span); // Patch up the function declaration, if necessary. match ast_proto_opt { None => { fn_ty.meta.purity = expected_purity; fn_ty.meta.proto = expected_proto; + fn_ty.meta.onceness = expected_onceness; } Some(_) => { } } @@ -2802,6 +2817,7 @@ fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) { meta: FnMeta {purity: ast::impure_fn, proto: ty::proto_vstore(ty::vstore_slice( ty::re_bound(ty::br_anon(0)))), + onceness: ast::on_many, bounds: @~[], ret_style: ast::return_val}, sig: FnSig {inputs: ~[{mode: ast::expl(ast::by_val), @@ -2825,6 +2841,7 @@ fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) { let fty = ty::mk_fn(tcx, FnTyBase { meta: FnMeta {purity: ast::impure_fn, proto: ty::proto_bare, + onceness: ast::on_many, bounds: @~[], ret_style: ast::return_val}, sig: FnSig {inputs: inputs, diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs index b82a55dc86bdf..6714e56491241 100644 --- a/src/rustc/middle/typeck/collect.rs +++ b/src/rustc/middle/typeck/collect.rs @@ -134,6 +134,7 @@ fn get_enum_variant_types(ccx: @crate_ctxt, result_ty = Some(ty::mk_fn(tcx, FnTyBase { meta: FnMeta {purity: ast::pure_fn, proto: ty::proto_vstore(ty::vstore_box), + onceness: ast::on_once, bounds: @~[], ret_style: ast::return_val}, sig: FnSig {inputs: args, @@ -604,7 +605,7 @@ fn convert_struct(ccx: @crate_ctxt, let t_dtor = ty::mk_fn( tcx, ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare, - ast::impure_fn, @~[], + ast::impure_fn, ast::on_once, @~[], ast_util::dtor_dec(), None, dtor.span)); write_ty_to_tcx(tcx, dtor.node.id, t_dtor); tcx.tcache.insert(local_def(dtor.node.id), @@ -643,6 +644,7 @@ fn convert_struct(ccx: @crate_ctxt, meta: FnMeta { purity: ast::pure_fn, proto: ty::proto_bare, + onceness: ast::on_once, bounds: @~[], ret_style: ast::return_val, }, @@ -682,9 +684,15 @@ fn ty_of_method(ccx: @crate_ctxt, rp: Option) -> ty::method { {ident: m.ident, tps: ty_param_bounds(ccx, m.tps), - fty: ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare, - m.purity, @~[], - m.decl, None, m.span), + fty: ty_of_fn_decl(ccx, + type_rscope(rp), + ast::proto_bare, + m.purity, + ast::on_once, + @~[], + m.decl, + None, + m.span), self_ty: m.self_ty.node, vis: m.vis, def_id: local_def(m.id)} @@ -696,8 +704,15 @@ fn ty_of_ty_method(self: @crate_ctxt, id: ast::def_id) -> ty::method { {ident: m.ident, tps: ty_param_bounds(self, m.tps), - fty: ty_of_fn_decl(self, type_rscope(rp), ast::proto_bare, m.purity, - @~[], m.decl, None, m.span), + fty: ty_of_fn_decl(self, + type_rscope(rp), + ast::proto_bare, + m.purity, + ast::on_once, + @~[], + m.decl, + None, + m.span), // assume public, because this is only invoked on trait methods self_ty: m.self_ty.node, vis: ast::public, @@ -752,9 +767,15 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item) } ast::item_fn(decl, purity, tps, _) => { let bounds = ty_param_bounds(ccx, tps); - let tofd = ty_of_fn_decl(ccx, empty_rscope, - ast::proto_bare, purity, @~[], - decl, None, it.span); + let tofd = ty_of_fn_decl(ccx, + empty_rscope, + ast::proto_bare, + purity, + ast::on_once, + @~[], + decl, + None, + it.span); let tpt = {bounds: bounds, region_param: None, ty: ty::mk_fn(ccx.tcx, tofd)}; @@ -910,6 +931,7 @@ fn ty_of_foreign_fn_decl(ccx: @crate_ctxt, let t_fn = ty::mk_fn(ccx.tcx, FnTyBase { meta: FnMeta {purity: purity, proto: ty::proto_bare, + onceness: ast::on_many, bounds: @~[], ret_style: ast::return_val}, sig: FnSig {inputs: input_tys, diff --git a/src/rustc/middle/typeck/infer/combine.rs b/src/rustc/middle/typeck/infer/combine.rs index 342a2ce2b76cd..859e948c7f9fe 100644 --- a/src/rustc/middle/typeck/infer/combine.rs +++ b/src/rustc/middle/typeck/infer/combine.rs @@ -46,6 +46,7 @@ use to_str::ToStr; use ty::{FnTyBase, FnMeta, FnSig}; +use syntax::ast::onceness; trait combine { fn infcx() -> infer_ctxt; @@ -72,6 +73,7 @@ trait combine { fn protos(p1: ty::fn_proto, p2: ty::fn_proto) -> cres; fn ret_styles(r1: ret_style, r2: ret_style) -> cres; fn purities(a: purity, b: purity) -> cres; + fn oncenesses(a: onceness, b: onceness) -> cres; fn contraregions(a: ty::Region, b: ty::Region) -> cres; fn regions(a: ty::Region, b: ty::Region) -> cres; fn vstores(vk: ty::terr_vstore_kind, @@ -311,10 +313,14 @@ fn super_fn_metas( do self.protos(a_f.proto, b_f.proto).chain |p| { do self.ret_styles(a_f.ret_style, b_f.ret_style).chain |rs| { do self.purities(a_f.purity, b_f.purity).chain |purity| { - Ok(FnMeta {purity: purity, - proto: p, - bounds: a_f.bounds, // XXX: This is wrong! - ret_style: rs}) + do self.oncenesses(a_f.onceness, b_f.onceness).chain + |onceness| { + Ok(FnMeta {purity: purity, + proto: p, + onceness: onceness, + bounds: a_f.bounds, // XXX: This is wrong! + ret_style: rs}) + } } } } diff --git a/src/rustc/middle/typeck/infer/glb.rs b/src/rustc/middle/typeck/infer/glb.rs index 77e753fa2204f..81cc6754ccb28 100644 --- a/src/rustc/middle/typeck/infer/glb.rs +++ b/src/rustc/middle/typeck/infer/glb.rs @@ -1,6 +1,7 @@ use combine::*; use lattice::*; use to_str::ToStr; +use syntax::ast::{on_many, on_once}; enum Glb = combine_fields; // "greatest lower bound" (common subtype) @@ -97,6 +98,13 @@ impl Glb: combine { } } + fn oncenesses(a: onceness, b: onceness) -> cres { + match (a, b) { + (on_once, _) | (_, on_once) => Ok(on_once), + (on_many, on_many) => Ok(on_many) + } + } + fn ret_styles(r1: ret_style, r2: ret_style) -> cres { match (r1, r2) { (ast::return_val, ast::return_val) => { diff --git a/src/rustc/middle/typeck/infer/lub.rs b/src/rustc/middle/typeck/infer/lub.rs index dcff863a126f0..101c40b679d93 100644 --- a/src/rustc/middle/typeck/infer/lub.rs +++ b/src/rustc/middle/typeck/infer/lub.rs @@ -1,6 +1,7 @@ use combine::*; use lattice::*; use to_str::ToStr; +use syntax::ast::{on_many, on_once}; enum Lub = combine_fields; // "subtype", "subregion" etc @@ -80,6 +81,13 @@ impl Lub: combine { } } + fn oncenesses(a: onceness, b: onceness) -> cres { + match (a, b) { + (on_many, _) | (_, on_many) => Ok(on_many), + (on_once, on_once) => Ok(on_once) + } + } + fn ret_styles(r1: ret_style, r2: ret_style) -> cres { match (r1, r2) { (ast::return_val, _) | diff --git a/src/rustc/middle/typeck/infer/sub.rs b/src/rustc/middle/typeck/infer/sub.rs index 3677911b5eaa1..1f77919e16e13 100644 --- a/src/rustc/middle/typeck/infer/sub.rs +++ b/src/rustc/middle/typeck/infer/sub.rs @@ -93,6 +93,12 @@ impl Sub: combine { }) } + fn oncenesses(a: onceness, b: onceness) -> cres { + self.lub().oncenesses(a, b).compare(b, || { + ty::terr_onceness_mismatch(expected_found(&self, a, b)) + }) + } + fn ret_styles(a: ret_style, b: ret_style) -> cres { self.lub().ret_styles(a, b).compare(b, || { ty::terr_ret_style_mismatch(expected_found(&self, a, b)) diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs index 75e21a5297b51..da06eadcc13d6 100644 --- a/src/rustc/util/ppaux.rs +++ b/src/rustc/util/ppaux.rs @@ -19,7 +19,8 @@ use syntax::codemap; use syntax::codemap::span; use syntax::print::pprust; use syntax::print::pprust::{path_to_str, proto_to_str, - mode_to_str, purity_to_str}; + mode_to_str, purity_to_str, + onceness_to_str}; use syntax::{ast, ast_util}; use syntax::ast_map; @@ -266,14 +267,24 @@ fn ty_to_str(cx: ctxt, typ: t) -> ~str { }; modestr + ty_to_str(cx, ty) } - fn fn_to_str(cx: ctxt, purity: ast::purity, proto: ty::fn_proto, + fn fn_to_str(cx: ctxt, + purity: ast::purity, + proto: ty::fn_proto, + onceness: ast::onceness, ident: Option, - inputs: ~[arg], output: t, cf: ast::ret_style) -> ~str { + inputs: ~[arg], + output: t, + cf: ast::ret_style) -> ~str { let mut s; s = match purity { - ast::impure_fn => ~"", - _ => purity_to_str(purity) + ~" " + ast::impure_fn => ~"", + _ => purity_to_str(purity) + ~" " + }; + + s += match onceness { + ast::on_many => ~"", + ast::on_once => onceness_to_str(onceness) + ~" " }; s += ~"fn"; @@ -298,8 +309,13 @@ fn ty_to_str(cx: ctxt, typ: t) -> ~str { } fn method_to_str(cx: ctxt, m: method) -> ~str { return fn_to_str( - cx, m.fty.meta.purity, m.fty.meta.proto, Some(m.ident), - m.fty.sig.inputs, m.fty.sig.output, + cx, + m.fty.meta.purity, + m.fty.meta.proto, + m.fty.meta.onceness, + Some(m.ident), + m.fty.sig.inputs, + m.fty.sig.output, m.fty.meta.ret_style) + ~";"; } fn field_to_str(cx: ctxt, f: field) -> ~str { @@ -347,8 +363,14 @@ fn ty_to_str(cx: ctxt, typ: t) -> ~str { ~"(" + str::connect(strs, ~",") + ~")" } ty_fn(ref f) => { - fn_to_str(cx, f.meta.purity, f.meta.proto, None, f.sig.inputs, - f.sig.output, f.meta.ret_style) + fn_to_str(cx, + f.meta.purity, + f.meta.proto, + f.meta.onceness, + None, + f.sig.inputs, + f.sig.output, + f.meta.ret_style) } ty_infer(infer_ty) => infer_ty.to_str(), ty_param({idx: id, _}) => { diff --git a/src/test/compile-fail/once-fn-subtyping.rs b/src/test/compile-fail/once-fn-subtyping.rs new file mode 100644 index 0000000000000..008cb56ab098b --- /dev/null +++ b/src/test/compile-fail/once-fn-subtyping.rs @@ -0,0 +1,5 @@ +fn main() { + let f: &fn() = ||(); + let g: &once fn() = f; //~ ERROR mismatched types +} + From a7a865ea5fae920ab559c253e280d297eb925c49 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 2 Nov 2012 16:03:47 -0700 Subject: [PATCH 2/2] rustc: Implement deriving involving generic bounded traits --- src/rustc/middle/trans/callee.rs | 2 +- src/rustc/middle/trans/deriving.rs | 29 +++- src/rustc/middle/ty.rs | 13 +- src/rustc/middle/typeck/deriving.rs | 139 +++++++++++++++--- src/test/run-pass/deriving-generic-bounded.rs | 33 +++++ 5 files changed, 180 insertions(+), 36 deletions(-) create mode 100644 src/test/run-pass/deriving-generic-bounded.rs diff --git a/src/rustc/middle/trans/callee.rs b/src/rustc/middle/trans/callee.rs index bd1facdddce70..796b426353565 100644 --- a/src/rustc/middle/trans/callee.rs +++ b/src/rustc/middle/trans/callee.rs @@ -153,7 +153,7 @@ fn trans_fn_ref_with_vtables_to_callee(bcx: block, fn trans_fn_ref_with_vtables( bcx: block, // def_id: ast::def_id, // def id of fn - ref_id: ast::node_id, // node id of use of fn + ref_id: ast::node_id, // node id of use of fn; may be zero if N/A type_params: ~[ty::t], // values for fn's ty params vtables: Option) -> FnData diff --git a/src/rustc/middle/trans/deriving.rs b/src/rustc/middle/trans/deriving.rs index 4410d79438084..bc398bf753df3 100644 --- a/src/rustc/middle/trans/deriving.rs +++ b/src/rustc/middle/trans/deriving.rs @@ -13,7 +13,8 @@ use middle::trans::common; use middle::trans::common::{C_bool, C_int, T_ptr, block, crate_ctxt}; use middle::trans::expr::SaveIn; use middle::trans::type_of::type_of; -use middle::typeck::{method_origin, method_static}; +use middle::ty::DerivedFieldInfo; +use middle::typeck::method_static; use syntax::ast; use syntax::ast::{def_id, ident, node_id, ty_param}; use syntax::ast_map::path; @@ -229,7 +230,7 @@ fn trans_deriving_enum_method(ccx: @crate_ctxt, llfn: ValueRef, } fn call_substructure_method(bcx: block, - derived_method_info: &method_origin, + derived_field_info: &DerivedFieldInfo, self_ty: ty::t, llselfval: ValueRef, llotherval: ValueRef) -> block { @@ -237,15 +238,29 @@ fn call_substructure_method(bcx: block, let ccx = fcx.ccx; let target_method_def_id; - match *derived_method_info { + match derived_field_info.method_origin { method_static(did) => target_method_def_id = did, _ => fail ~"derived method didn't resolve to a static method" } - let fn_expr_ty = ty::lookup_item_type(ccx.tcx, target_method_def_id).ty; + let fn_expr_tpbt = ty::lookup_item_type(ccx.tcx, target_method_def_id); + debug!("(calling substructure method) substructure method has %u \ + parameter(s), vtable result is %?", + fn_expr_tpbt.bounds.len(), + derived_field_info.vtable_result); + + // Get the substructure method we need to call. This may involve + // code generation in the case of generics, default methods, or cross- + // crate inlining. + let fn_data = callee::trans_fn_ref_with_vtables(bcx, + target_method_def_id, + 0, // ref id + *derived_field_info. + type_parameter_substitutions, + derived_field_info. + vtable_result); + let llfn = fn_data.llfn; - // XXX: Cross-crate won't work! - let llfn = get_item_val(ccx, target_method_def_id.node); let cb: &fn(block) -> Callee = |block| { Callee { bcx: block, @@ -260,7 +275,7 @@ fn call_substructure_method(bcx: block, callee::trans_call_inner(bcx, None, - fn_expr_ty, + fn_expr_tpbt.ty, ty::mk_bool(ccx.tcx), cb, ArgVals(~[llotherval]), diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 848f8a3a1dd06..b2646126f1afd 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -203,6 +203,7 @@ export trait_supertraits; export AutoAdjustment; export AutoRef, AutoRefKind, AutoSlice, AutoPtr; export DerivedMethodInfo; +export DerivedFieldInfo; // Data types @@ -341,6 +342,12 @@ struct DerivedMethodInfo { containing_impl: ast::def_id } +struct DerivedFieldInfo { + method_origin: typeck::method_origin, + type_parameter_substitutions: @~[ty::t], + vtable_result: Option +} + type ctxt = @{diag: syntax::diagnostic::span_handler, interner: HashMap, @@ -386,13 +393,11 @@ type ctxt = legacy_boxed_traits: HashMap, provided_method_sources: HashMap, supertraits: HashMap, - deriving_struct_methods: HashMap, + deriving_struct_methods: HashMap, // The outer vector here describes each enum variant, while the inner // nested vector describes each enum variant argument. - deriving_enum_methods: HashMap, + deriving_enum_methods: HashMap, // A mapping from the def ID of a method that was automatically derived // to information about it. diff --git a/src/rustc/middle/typeck/deriving.rs b/src/rustc/middle/typeck/deriving.rs index c48c8576e079a..7fa44491a872e 100644 --- a/src/rustc/middle/typeck/deriving.rs +++ b/src/rustc/middle/typeck/deriving.rs @@ -11,47 +11,112 @@ use syntax::ast::item_impl; use syntax::ast::node_id; use syntax::ast::self_ty_; use syntax::ast::trait_ref; -use syntax::ast_util::def_id_of_def; +use syntax::ast_util::{def_id_of_def, dummy_sp}; use syntax::codemap::span; use syntax::print::pprust; use syntax::visit::{default_simple_visitor, mk_simple_visitor, visit_crate}; use middle::resolve::{Impl, MethodInfo}; use middle::ty; -use middle::ty::{substs, ty_class, ty_enum, ty_param_bounds_and_ty}; +use middle::ty::{DerivedFieldInfo, substs, ty_class, ty_enum}; +use middle::ty::{ty_param_bounds_and_ty}; use /*middle::typeck::*/check::method; +use /*middle::typeck::*/check::vtable; use /*middle::typeck::*/infer::infer_ctxt; +use /*middle::typeck::*/vtable::{LocationInfo, VtableContext}; +use util::ppaux; + +struct MethodMatch { + method_def_id: def_id, + type_parameter_substitutions: @~[ty::t], + vtable_result: Option +} struct DerivingChecker { - crate_context: @crate_ctxt, - inference_context: infer_ctxt + crate_context: @crate_ctxt } fn DerivingChecker_new(crate_context: @crate_ctxt) -> DerivingChecker { DerivingChecker { crate_context: crate_context, - inference_context: infer::new_infer_ctxt(crate_context.tcx) } } +struct TyParamSubstsAndVtableResult { + type_parameter_substitutions: @~[ty::t], + vtable_result: Option +} + impl DerivingChecker { /// Matches one substructure type against an implementation. fn match_impl_method(impl_info: @Impl, substructure_type: ty::t, - method_info: @MethodInfo) -> bool { - // XXX: Generics and regions are not handled properly. + method_info: @MethodInfo, + span: span) -> + Option { let tcx = self.crate_context.tcx; - let impl_self_ty = ty::lookup_item_type(tcx, impl_info.did).ty; + + let impl_self_tpbt = ty::lookup_item_type(tcx, impl_info.did); let transformed_type = method::transform_self_type_for_method( - tcx, None, impl_self_ty, method_info.self_type); - return infer::can_mk_subty(self.inference_context, - substructure_type, - transformed_type).is_ok(); + tcx, None, impl_self_tpbt.ty, method_info.self_type); + + let inference_context = infer::new_infer_ctxt(self.crate_context.tcx); + let substs = { + self_r: None, + self_ty: None, + tps: inference_context.next_ty_vars(impl_self_tpbt.bounds.len()) + }; + let transformed_type = ty::subst( + self.crate_context.tcx, &substs, transformed_type); + + debug!("(matching impl method) substructure type %s, transformed \ + type %s, subst tps %u", + ppaux::ty_to_str(self.crate_context.tcx, substructure_type), + ppaux::ty_to_str(self.crate_context.tcx, transformed_type), + substs.tps.len()); + + if !infer::mk_subty(inference_context, + true, + ast_util::dummy_sp(), + substructure_type, + transformed_type).is_ok() { + return None; + } + + // Get the vtables. + let vtable_result; + if substs.tps.len() == 0 { + vtable_result = None; + } else { + let vcx = VtableContext { + ccx: self.crate_context, + infcx: inference_context + }; + let location_info = LocationInfo { + span: span, + id: impl_info.did.node + }; + vtable_result = Some(vtable::lookup_vtables(&vcx, + &location_info, + impl_self_tpbt.bounds, + &substs, + false, + false)); + } + + // Extract the type parameter substitutions. + let type_parameter_substitutions = @substs.tps.map(|ty_var| + inference_context.resolve_type_vars_if_possible(*ty_var)); + + Some(TyParamSubstsAndVtableResult { + type_parameter_substitutions: type_parameter_substitutions, + vtable_result: vtable_result + }) } fn check_deriving_for_substructure_type(substructure_type: ty::t, trait_ref: @trait_ref, impl_span: span) -> - Option { + Option { let tcx = self.crate_context.tcx; let sess = tcx.sess; let coherence_info = self.crate_context.coherence_info; @@ -64,12 +129,25 @@ impl DerivingChecker { Some(impls) => { // Try to unify each of these impls with the substructure // type. - for impls.each |impl_info| { - for impl_info.methods.each |method_info| { - if self.match_impl_method(*impl_info, - substructure_type, - *method_info) { - return Some(method_info.did); + // + // NB: Using range to avoid a recursive-use-of-dvec error. + for uint::range(0, impls.len()) |i| { + let impl_info = impls[i]; + for uint::range(0, impl_info.methods.len()) |j| { + let method_info = impl_info.methods[j]; + match self.match_impl_method(impl_info, + substructure_type, + method_info, + trait_ref.path.span) { + Some(move result) => { + return Some(MethodMatch { + method_def_id: method_info.did, + type_parameter_substitutions: + result.type_parameter_substitutions, + vtable_result: result.vtable_result + }); + } + None => {} // Continue. } } } @@ -91,8 +169,15 @@ impl DerivingChecker { match self.check_deriving_for_substructure_type(field_type, trait_ref, impl_span) { - Some(method_target_def_id) => { - field_info.push(method_static(method_target_def_id)); + Some(method_match) => { + field_info.push(DerivedFieldInfo { + method_origin: + method_static(method_match.method_def_id), + type_parameter_substitutions: + method_match.type_parameter_substitutions, + vtable_result: + method_match.vtable_result + }); } None => { let trait_str = pprust::path_to_str( @@ -127,9 +212,15 @@ impl DerivingChecker { for enum_variant_info.args.eachi |i, variant_arg_type| { match self.check_deriving_for_substructure_type( *variant_arg_type, trait_ref, impl_span) { - Some(method_target_def_id) => { - variant_methods.push(method_static( - method_target_def_id)); + Some(method_match) => { + variant_methods.push(DerivedFieldInfo { + method_origin: + method_static(method_match.method_def_id), + type_parameter_substitutions: + method_match.type_parameter_substitutions, + vtable_result: + method_match.vtable_result + }); } None => { let trait_str = pprust::path_to_str( diff --git a/src/test/run-pass/deriving-generic-bounded.rs b/src/test/run-pass/deriving-generic-bounded.rs new file mode 100644 index 0000000000000..94ecb476584d4 --- /dev/null +++ b/src/test/run-pass/deriving-generic-bounded.rs @@ -0,0 +1,33 @@ +trait MyEq { + pure fn eq(other: &self) -> bool; +} + +impl int : MyEq { + pure fn eq(other: &int) -> bool { + self == *other + } +} + +impl @T : MyEq { + pure fn eq(other: &@T) -> bool { + unsafe { + io::println("@T"); + } + (*self).eq(&**other) + } +} + +struct A { + x: @int, + y: @int +} + +impl A : MyEq; + +fn main() { + let a = A { x: @3, y: @5 }; + let b = A { x: @10, y: @20 }; + assert a.eq(&a); + assert !a.eq(&b); +} +