From 5e7229b72cb0a7fc1b6a9dd4815517d6a4bb2127 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 9 May 2012 06:09:58 -0700 Subject: [PATCH] reduce self type to a special type parameter --- src/rustc/metadata/tydecode.rs | 11 +- src/rustc/metadata/tyencode.rs | 7 +- src/rustc/middle/infer.rs | 58 ++++- src/rustc/middle/trans/common.rs | 4 +- src/rustc/middle/trans/impl.rs | 2 +- src/rustc/middle/trans/shape.rs | 2 +- src/rustc/middle/trans/type_of.rs | 7 +- src/rustc/middle/ty.rs | 211 ++++++++--------- src/rustc/middle/typeck.rs | 249 +++++++------------- src/rustc/util/ppaux.rs | 6 +- src/test/compile-fail/issue-2149.rs | 5 +- src/test/compile-fail/selftype-astparam.rs | 15 ++ src/test/compile-fail/selftype-ifacetype.rs | 9 + src/test/run-pass/monad.rs | 8 +- src/test/run-pass/selftype-add-ints.rs | 19 ++ 15 files changed, 289 insertions(+), 324 deletions(-) create mode 100644 src/test/compile-fail/selftype-astparam.rs create mode 100644 src/test/compile-fail/selftype-ifacetype.rs create mode 100644 src/test/run-pass/selftype-add-ints.rs diff --git a/src/rustc/metadata/tydecode.rs b/src/rustc/metadata/tydecode.rs index 7f627e39039..644a38b8c8d 100644 --- a/src/rustc/metadata/tydecode.rs +++ b/src/rustc/metadata/tydecode.rs @@ -199,12 +199,16 @@ fn parse_vstore(st: @pstate) -> ty::vstore { fn parse_substs(st: @pstate, conv: conv_did) -> ty::substs { let self_r = parse_opt(st) {|| parse_region(st) }; + let self_ty = parse_opt(st) {|| parse_ty(st, conv) }; + assert next(st) == '['; let mut params: [ty::t] = []; while peek(st) != ']' { params += [parse_ty(st, conv)]; } st.pos = st.pos + 1u; - ret {self_r: self_r, tps: params}; + ret {self_r: self_r, + self_ty: self_ty, + tps: params}; } fn parse_bound_region(st: @pstate) -> ty::bound_region { @@ -298,10 +302,7 @@ fn parse_ty(st: @pstate, conv: conv_did) -> ty::t { ret ty::mk_param(st.tcx, parse_int(st) as uint, did); } 's' { - assert next(st) == '['; - let substs = parse_substs(st, conv); - assert next(st) == ']'; - ret ty::mk_self(st.tcx, substs); + ret ty::mk_self(st.tcx); } '@' { ret ty::mk_box(st.tcx, parse_mt(st, conv)); } '~' { ret ty::mk_uniq(st.tcx, parse_mt(st, conv)); } diff --git a/src/rustc/metadata/tyencode.rs b/src/rustc/metadata/tyencode.rs index 130e7f40916..4f42ab4e100 100644 --- a/src/rustc/metadata/tyencode.rs +++ b/src/rustc/metadata/tyencode.rs @@ -115,6 +115,7 @@ fn enc_opt(w: io::writer, t: option, enc_f: fn(T)) { fn enc_substs(w: io::writer, cx: @ctxt, substs: ty::substs) { enc_opt(w, substs.self_r) { |r| enc_region(w, cx, r) } + enc_opt(w, substs.self_ty) { |t| enc_ty(w, cx, t) } w.write_char('['); for substs.tps.each { |t| enc_ty(w, cx, t); } w.write_char(']'); @@ -281,10 +282,8 @@ fn enc_sty(w: io::writer, cx: @ctxt, st: ty::sty) { w.write_char('|'); w.write_str(uint::str(id)); } - ty::ty_self(substs) { - w.write_str("s["/&); - enc_substs(w, cx, substs); - w.write_char(']'); + ty::ty_self { + w.write_char('s'); } ty::ty_type { w.write_char('Y'); } ty::ty_opaque_closure_ptr(ty::ck_block) { w.write_str("C&"/&); } diff --git a/src/rustc/middle/infer.rs b/src/rustc/middle/infer.rs index 0477f6469cc..cd46f53c357 100644 --- a/src/rustc/middle/infer.rs +++ b/src/rustc/middle/infer.rs @@ -630,12 +630,15 @@ impl methods for resolve_state { fn resolve1(typ: ty::t) -> ty::t { #debug("Resolve1(%s)", typ.to_str(self.infcx)); indent(fn&() -> ty::t { - if !ty::get(typ).has_vars { ret typ; } + if !ty::type_needs_infer(typ) { ret typ; } - let tb = ty::get(typ); - alt tb.struct { - ty::ty_var(vid) { self.resolve_ty_var(vid) } - _ if !tb.has_regions && !self.deep { typ } + alt ty::get(typ).struct { + ty::ty_var(vid) { + self.resolve_ty_var(vid) + } + _ if !ty::type_has_regions(typ) && !self.deep { + typ + } _ { ty::fold_regions_and_ty( self.infcx.tcx, typ, @@ -935,6 +938,7 @@ iface combine { fn contratys(a: ty::t, b: ty::t) -> cres; fn tys(a: ty::t, b: ty::t) -> cres; fn tps(as: [ty::t], bs: [ty::t]) -> cres<[ty::t]>; + fn self_tys(a: option, b: option) -> cres>; fn substs(as: ty::substs, bs: ty::substs) -> cres; fn fns(a: ty::fn_ty, b: ty::fn_ty) -> cres; fn flds(a: ty::field, b: ty::field) -> cres; @@ -982,8 +986,10 @@ fn super_substs( } self.tps(a.tps, b.tps).chain { |tps| - eq_opt_regions(self.infcx(), a.self_r, b.self_r).chain { |self_r| - ok({self_r: self_r, tps: tps}) + self.self_tys(a.self_ty, b.self_ty).chain { |self_ty| + eq_opt_regions(self.infcx(), a.self_r, b.self_r).chain { |self_r| + ok({self_r: self_r, self_ty: self_ty, tps: tps}) + } } } } @@ -995,6 +1001,7 @@ fn super_tps( // (otherwise the type system would be unsound). In the // future we could allow type parameters to declare a // variance. + if check vec::same_length(as, bs) { iter2(as, bs) {|a, b| self.infcx().eq_tys(a, b) }.then {|| ok(as) @@ -1004,6 +1011,31 @@ fn super_tps( } } +fn super_self_tys( + self: C, a: option, b: option) -> cres> { + + // Note: the self type parameter is (currently) always treated as + // *invariant* (otherwise the type system would be unsound). + + alt (a, b) { + (none, none) { + ok(none) + } + (some(a), some(b)) { + self.infcx().eq_tys(a, b).then {|| + ok(some(a)) + } + } + (none, some(_)) | + (some(_), none) { + // I think it should never happen that we unify two substs and + // one of them has a self_ty and one doesn't...? I could be + // wrong about this. + err(ty::terr_self_substs) + } + } +} + fn super_flds( self: C, a: ty::field, b: ty::field) -> cres { @@ -1374,6 +1406,10 @@ impl of combine for sub { fn tps(as: [ty::t], bs: [ty::t]) -> cres<[ty::t]> { super_tps(self, as, bs) } + + fn self_tys(a: option, b: option) -> cres> { + super_self_tys(self, a, b) + } } impl of combine for lub { @@ -1549,6 +1585,10 @@ impl of combine for lub { fn tps(as: [ty::t], bs: [ty::t]) -> cres<[ty::t]> { super_tps(self, as, bs) } + + fn self_tys(a: option, b: option) -> cres> { + super_self_tys(self, a, b) + } } impl of combine for glb { @@ -1739,6 +1779,10 @@ impl of combine for glb { fn tps(as: [ty::t], bs: [ty::t]) -> cres<[ty::t]> { super_tps(self, as, bs) } + + fn self_tys(a: option, b: option) -> cres> { + super_self_tys(self, a, b) + } } // ______________________________________________________________________ diff --git a/src/rustc/middle/trans/common.rs b/src/rustc/middle/trans/common.rs index 412bacbda1d..b7f360cd337 100644 --- a/src/rustc/middle/trans/common.rs +++ b/src/rustc/middle/trans/common.rs @@ -916,7 +916,9 @@ fn field_idx_strict(cx: ty::ctxt, sp: span, ident: ast::ident, } fn dummy_substs(tps: [ty::t]) -> ty::substs { - {self_r: some(ty::re_bound(ty::br_self)), tps: tps} + {self_r: some(ty::re_bound(ty::br_self)), + self_ty: none, + tps: tps} } // diff --git a/src/rustc/middle/trans/impl.rs b/src/rustc/middle/trans/impl.rs index 5f50bb283a1..ec49a0ec7cb 100644 --- a/src/rustc/middle/trans/impl.rs +++ b/src/rustc/middle/trans/impl.rs @@ -256,7 +256,7 @@ fn make_impl_vtable(ccx: @crate_ctxt, impl_id: ast::def_id, substs: [ty::t], let has_tps = (*ty::lookup_item_type(ccx.tcx, impl_id).bounds).len() > 0u; make_vtable(ccx, vec::map(*ty::iface_methods(tcx, ifce_id)) {|im| let fty = ty::subst_tps(tcx, substs, ty::mk_fn(tcx, im.fty)); - if (*im.tps).len() > 0u || ty::type_has_vars(fty) { + if (*im.tps).len() > 0u || ty::type_has_self(fty) { C_null(T_ptr(T_nil())) } else { let m_id = method_with_name(ccx, impl_id, im.ident); diff --git a/src/rustc/middle/trans/shape.rs b/src/rustc/middle/trans/shape.rs index ce6afcd844d..14215efb199 100644 --- a/src/rustc/middle/trans/shape.rs +++ b/src/rustc/middle/trans/shape.rs @@ -457,7 +457,7 @@ fn shape_of(ccx: @crate_ctxt, t: ty::t, ty_param_map: [uint]) -> [u8] { ty::ty_fn({proto: ast::proto_bare, _}) { [shape_bare_fn] } ty::ty_opaque_closure_ptr(_) { [shape_opaque_closure_ptr] } ty::ty_constr(inner_t, _) { shape_of(ccx, inner_t, ty_param_map) } - ty::ty_var(_) | ty::ty_self(_) { + ty::ty_var(_) | ty::ty_self { ccx.sess.bug("shape_of: unexpected type struct found"); } } diff --git a/src/rustc/middle/trans/type_of.rs b/src/rustc/middle/trans/type_of.rs index b20f5bc368a..16646472de8 100644 --- a/src/rustc/middle/trans/type_of.rs +++ b/src/rustc/middle/trans/type_of.rs @@ -44,7 +44,7 @@ fn type_of_fn_from_ty(cx: @crate_ctxt, fty: ty::t) -> TypeRef { } fn type_of_non_gc_box(cx: @crate_ctxt, t: ty::t) -> TypeRef { - assert !ty::type_has_vars(t); + assert !ty::type_needs_infer(t); let t_norm = ty::normalize_ty(cx.tcx, t); if t != t_norm { @@ -62,7 +62,7 @@ fn type_of_non_gc_box(cx: @crate_ctxt, t: ty::t) -> TypeRef { } fn type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef { - assert !ty::type_has_vars(t); + assert !ty::type_needs_infer(t); #debug("type_of %?: %?", t, ty::get(t)); @@ -149,8 +149,7 @@ fn type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef { }; T_struct(tys) } - ty::ty_self(_) { cx.tcx.sess.unimpl("type_of: ty_self \ - not implemented"); } + ty::ty_self { cx.tcx.sess.unimpl("type_of: ty_self"); } ty::ty_var(_) { cx.tcx.sess.bug("type_of shouldn't see a ty_var"); } } }; diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index fdbff904407..87daf62d800 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -35,7 +35,7 @@ export expr_ty; export expr_ty_params_and_ty; export expr_is_lval; export field_ty; -export fold_ty, fold_sty_to_ty, fold_region, fold_regions, fold_ty_var; +export fold_ty, fold_sty_to_ty, fold_region, fold_regions; export fold_regions_and_ty, walk_regions_and_ty; export field; export field_idx; @@ -61,7 +61,7 @@ export sequence_element_type; export sort_methods; export stmt_node_id; export sty; -export subst, subst_tps, substs_is_noop, substs; +export subst, subst_tps, substs_is_noop, substs_to_str, substs; export t; export new_ty_hash; export enum_variants, substd_enum_variants; @@ -97,10 +97,11 @@ export ty_type, mk_type; export ty_uint, mk_uint, mk_mach_uint; export ty_uniq, mk_uniq, mk_imm_uniq, type_is_unique_box; export ty_var, mk_var, type_is_var; -export ty_self, mk_self; +export ty_self, mk_self, type_has_self; export region, bound_region; -export get, type_has_params, type_has_vars, type_has_regions; +export get, type_has_params, type_needs_infer, type_has_regions; export type_has_resources, type_id; +export tbox_has_flag; export ty_var_id; export ty_to_def_id; export ty_fn_args; @@ -230,12 +231,21 @@ type ctxt = borrowings: hashmap, normalized_cache: hashmap}; +enum tbox_flag { + has_params = 1, + has_self = 2, + needs_infer = 4, + has_regions = 8, + has_resources = 16, + + // a meta-flag: subst may be required if the type has parameters, a self + // type, or references bound regions + needs_subst = 1 | 2 | 8 +} + type t_box = @{struct: sty, id: uint, - has_params: bool, - has_vars: bool, - has_regions: bool, - has_resources: bool, + flags: uint, o_def_id: option}; // To reduce refcounting cost, we're representing types as unsafe pointers @@ -253,10 +263,14 @@ pure fn get(t: t) -> t_box unsafe { t3 } -fn type_has_params(t: t) -> bool { get(t).has_params } -fn type_has_vars(t: t) -> bool { get(t).has_vars } -fn type_has_regions(t: t) -> bool { get(t).has_regions } -fn type_has_resources(t: t) -> bool { get(t).has_resources } +fn tbox_has_flag(tb: t_box, flag: tbox_flag) -> bool { + (tb.flags & (flag as uint)) != 0u +} +fn type_has_params(t: t) -> bool { tbox_has_flag(get(t), has_params) } +fn type_has_self(t: t) -> bool { tbox_has_flag(get(t), has_self) } +fn type_needs_infer(t: t) -> bool { tbox_has_flag(get(t), needs_infer) } +fn type_has_regions(t: t) -> bool { tbox_has_flag(get(t), has_regions) } +fn type_has_resources(t: t) -> bool { tbox_has_flag(get(t), has_resources) } fn type_def_id(t: t) -> option { get(t).o_def_id } fn type_id(t: t) -> uint { get(t).id } @@ -301,6 +315,7 @@ type opt_region = option; // `&self.T` within the type and report an error. type substs = { self_r: opt_region, + self_ty: option, tps: [t] }; @@ -331,7 +346,7 @@ enum sty { ty_var(ty_vid), // type variable during typechecking ty_param(uint, def_id), // type parameter - ty_self(substs), // interface method self type + ty_self, // special, implicit `self` type parameter ty_type, // type_desc* ty_opaque_box, // used by monomorphizer to represent any @ box @@ -371,7 +386,8 @@ enum type_err { terr_regions_differ(region, region), terr_vstores_differ(terr_vstore_kind, vstore, vstore), terr_in_field(@type_err, str), - terr_sorts(t, t) + terr_sorts(t, t), + terr_self_substs } enum param_bound { @@ -480,95 +496,65 @@ fn mk_t_with_id(cx: ctxt, st: sty, o_def_id: option) -> t { some(t) { unsafe { ret unsafe::reinterpret_cast(t); } } _ {} } - let mut has_params = false, has_vars = false, has_regions = false, - has_resources = false; - fn derive_flags(&has_params: bool, &has_vars: bool, &has_regions: bool, - &has_resources: bool, tt: t) { - let t = get(tt); - has_params |= t.has_params; - has_vars |= t.has_vars; - has_regions |= t.has_regions; - has_resources |= t.has_resources; - } - fn derive_rflags(&has_vars: bool, &has_regions: bool, r: region) { - has_regions = true; - alt r { - ty::re_var(_) { has_vars = true; } - _ { } + let mut flags = 0u; + fn rflags(r: region) -> uint { + (has_regions as uint) | { + alt r { + ty::re_var(_) {needs_infer as uint} + _ {0u} + } } } - fn derive_sflags(&has_params: bool, &has_vars: bool, &has_regions: bool, - &has_resources: bool, substs: substs) { - for substs.tps.each {|tt| - derive_flags(has_params, has_vars, has_regions, - has_resources, tt); - } - substs.self_r.iter { |r| derive_rflags(has_vars, has_regions, r) } + fn sflags(substs: substs) -> uint { + let mut f = 0u; + for substs.tps.each {|tt| f |= get(tt).flags; } + substs.self_r.iter { |r| f |= rflags(r) } + ret f; } alt st { ty_estr(vstore_slice(r)) { - derive_rflags(has_vars, has_regions, r); + flags |= rflags(r); } ty_evec(mt, vstore_slice(r)) { - derive_rflags(has_vars, has_regions, r); - derive_flags(has_params, has_vars, has_regions, - has_resources, mt.ty); + flags |= rflags(r); + flags |= get(mt.ty).flags; } ty_nil | ty_bot | ty_bool | ty_int(_) | ty_float(_) | ty_uint(_) | ty_str | ty_estr(_) | ty_type | ty_opaque_closure_ptr(_) | ty_opaque_box {} - ty_param(_, _) { has_params = true; } - ty_var(_) | ty_self(_) { has_vars = true; } + ty_param(_, _) { flags |= has_params as uint; } + ty_var(_) { flags |= needs_infer as uint; } + ty_self { flags |= has_self as uint; } ty_enum(_, substs) | ty_class(_, substs) | ty_iface(_, substs) { - derive_sflags(has_params, has_vars, has_regions, - has_resources, substs); + flags |= sflags(substs); } ty_box(m) | ty_uniq(m) | ty_vec(m) | ty_evec(m, _) | ty_ptr(m) { - derive_flags(has_params, has_vars, has_regions, - has_resources, m.ty); + flags |= get(m.ty).flags; } ty_rptr(r, m) { - derive_rflags(has_vars, has_regions, r); - derive_flags(has_params, has_vars, has_regions, - has_resources, m.ty); + flags |= rflags(r); + flags |= get(m.ty).flags; } ty_rec(flds) { - for flds.each {|f| - derive_flags(has_params, has_vars, has_regions, - has_resources, f.mt.ty); - } + for flds.each {|f| flags |= get(f.mt.ty).flags; } } ty_tup(ts) { - for ts.each {|tt| derive_flags(has_params, has_vars, - has_regions, has_resources, tt); } + for ts.each {|tt| flags |= get(tt).flags; } } ty_fn(f) { - for f.inputs.each {|a| - derive_flags(has_params, has_vars, has_regions, - has_resources, a.ty); - } - derive_flags(has_params, has_vars, has_regions, - has_resources, f.output); + for f.inputs.each {|a| flags |= get(a.ty).flags; } + flags |= get(f.output).flags; } ty_res(_, tt, substs) { - has_resources = true; - derive_flags(has_params, has_vars, has_regions, - has_resources, tt); - derive_sflags(has_params, has_vars, has_regions, - has_resources, substs); + flags |= (has_resources as uint); + flags |= get(tt).flags; + flags |= sflags(substs); } ty_constr(tt, _) { - derive_flags(has_params, has_vars, has_regions, - has_resources, tt); + flags |= get(tt).flags; } } - let t = @{struct: st, - id: cx.next_id, - has_params: has_params, - has_vars: has_vars, - has_regions: has_regions, - has_resources: has_resources, - o_def_id: o_def_id}; + let t = @{struct: st, id: cx.next_id, flags: flags, o_def_id: o_def_id}; cx.interner.insert(key, t); cx.next_id += 1u; unsafe { unsafe::reinterpret_cast(t) } @@ -660,7 +646,7 @@ fn mk_res(cx: ctxt, did: ast::def_id, fn mk_var(cx: ctxt, v: ty_vid) -> t { mk_t(cx, ty_var(v)) } -fn mk_self(cx: ctxt, substs: substs) -> t { mk_t(cx, ty_self(substs)) } +fn mk_self(cx: ctxt) -> t { mk_t(cx, ty_self) } fn mk_param(cx: ctxt, n: uint, k: def_id) -> t { mk_t(cx, ty_param(n, k)) } @@ -699,14 +685,15 @@ fn maybe_walk_ty(ty: t, f: fn(t) -> bool) { if !f(ty) { ret; } alt get(ty).struct { ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | - ty_str | ty_estr(_) | ty_type | ty_opaque_box | - ty_opaque_closure_ptr(_) | ty_var(_) | ty_param(_, _) {} + ty_str | ty_estr(_) | ty_type | ty_opaque_box | ty_self | + ty_opaque_closure_ptr(_) | ty_var(_) | ty_param(_, _) { + } ty_box(tm) | ty_vec(tm) | ty_evec(tm, _) | ty_ptr(tm) | ty_rptr(_, tm) { maybe_walk_ty(tm.ty, f); } ty_enum(_, substs) | ty_class(_, substs) | - ty_iface(_, substs) | ty_self(substs) { + ty_iface(_, substs) { for substs.tps.each {|subty| maybe_walk_ty(subty, f); } } ty_rec(fields) { @@ -733,6 +720,7 @@ fn fold_sty_to_ty(tcx: ty::ctxt, sty: sty, foldop: fn(t) -> t) -> t { fn fold_sty(sty: sty, fldop: fn(t) -> t) -> sty { fn fold_substs(substs: substs, fldop: fn(t) -> t) -> substs { {self_r: substs.self_r, + self_ty: substs.self_ty.map { |t| fldop(t) }, tps: substs.tps.map { |t| fldop(t) }} } @@ -758,9 +746,6 @@ fn fold_sty(sty: sty, fldop: fn(t) -> t) -> sty { ty_iface(did, substs) { ty_iface(did, fold_substs(substs, fldop)) } - ty_self(substs) { - ty_self(fold_substs(substs, fldop)) - } ty_rec(fields) { let new_fields = vec::map(fields) {|fl| let new_ty = fldop(fl.mt.ty); @@ -796,7 +781,7 @@ fn fold_sty(sty: sty, fldop: fn(t) -> t) -> sty { } ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | ty_str | ty_estr(_) | ty_type | ty_opaque_closure_ptr(_) | - ty_opaque_box | ty_var(_) | ty_param(_, _) { + ty_opaque_box | ty_var(_) | ty_param(*) | ty_self { sty } } @@ -808,15 +793,6 @@ fn fold_ty(cx: ctxt, t0: t, fldop: fn(t) -> t) -> t { fldop(mk_t(cx, sty)) } -fn fold_ty_var(cx: ctxt, t0: t, fldop: fn(ty_vid) -> t) -> t { - let tb = get(t0); - if !tb.has_vars { ret t0; } - alt tb.struct { - ty_var(id) { fldop(id) } - sty { fold_sty_to_ty(cx, sty) {|t| fold_ty_var(cx, t, fldop) } } - } -} - fn walk_regions_and_ty( cx: ctxt, ty: t, @@ -845,6 +821,7 @@ fn fold_regions_and_ty( fldt: fn(t: t) -> t) -> substs { {self_r: substs.self_r.map { |r| fldr(r) }, + self_ty: substs.self_ty.map { |t| fldt(t) }, tps: substs.tps.map { |t| fldt(t) }} } @@ -873,9 +850,6 @@ fn fold_regions_and_ty( ty_iface(def_id, substs) { ty::mk_iface(cx, def_id, fold_substs(substs, fldr, fldt)) } - ty_self(substs) { - ty::mk_self(cx, fold_substs(substs, fldr, fldt)) - } ty_res(def_id, t, substs) { ty::mk_res(cx, def_id, fldt(t), fold_substs(substs, fldr, fldt)) @@ -916,7 +890,7 @@ fn fold_region(cx: ctxt, t0: t, fldop: fn(region, bool) -> region) -> t { fn do_fold(cx: ctxt, t0: t, under_r: bool, fldop: fn(region, bool) -> region) -> t { let tb = get(t0); - if !tb.has_regions { ret t0; } + if !tbox_has_flag(tb, has_regions) { ret t0; } alt tb.struct { ty_rptr(r, {ty: t1, mutbl: m}) { let m_r = fldop(r, under_r); @@ -951,7 +925,7 @@ fn fold_region(cx: ctxt, t0: t, fldop: fn(region, bool) -> region) -> t { fn subst_tps(cx: ctxt, tps: [t], typ: t) -> t { if tps.len() == 0u { ret typ; } let tb = ty::get(typ); - if !tb.has_params { ret typ; } + if !tbox_has_flag(tb, has_params) { ret typ; } alt tb.struct { ty_param(idx, _) { tps[idx] } sty { fold_sty_to_ty(cx, sty) {|t| subst_tps(cx, tps, t) } } @@ -959,12 +933,15 @@ fn subst_tps(cx: ctxt, tps: [t], typ: t) -> t { } fn substs_is_noop(substs: substs) -> bool { - substs.tps.len() == 0u && substs.self_r.is_none() + substs.tps.len() == 0u && + substs.self_r.is_none() && + substs.self_ty.is_none() } fn substs_to_str(cx: ctxt, substs: substs) -> str { - #fmt["substs(self_r=%s, tps=%?)", + #fmt["substs(self_r=%s, self_ty=%s, tps=%?)", substs.self_r.map_default("none", { |r| region_to_str(cx, r) }), + substs.self_ty.map_default("none", { |t| ty_to_str(cx, t) }), substs.tps.map { |t| ty_to_str(cx, t) }] } @@ -985,20 +962,17 @@ fn subst(cx: ctxt, substs: substs, typ: t) -> t { let tb = get(typ); - if !tb.has_params && !tb.has_regions { ret typ; } + if !tbox_has_flag(tb, needs_subst) { ret typ; } alt tb.struct { - ty_param(idx, _) { substs.tps[idx] } + ty_param(idx, _) {substs.tps[idx]} + ty_self {substs.self_ty.get()} _ { fold_regions_and_ty( cx, typ, { |r| alt r { - re_bound(br_self) { - option::get(substs.self_r) - } - _ { - r - } + re_bound(br_self) {substs.self_r.get()} + _ {r} } }, { |t| do_subst(cx, substs, t) }, @@ -1406,14 +1380,14 @@ fn type_kind(cx: ctxt, ty: t) -> kind { } lowest } - // Resources are always noncopyable. ty_res(did, inner, tps) { kind_noncopyable } ty_param(_, did) { param_bounds_to_kind(cx.ty_param_bounds.get(did.node)) } ty_constr(t, _) { type_kind(cx, t) } - ty_var(_) { fail "FIXME"; } - ty_self(_) { kind_noncopyable } + ty_self { kind_noncopyable } + + ty_var(_) { cx.sess.bug("Asked to compute kind of a type variable"); } }; cx.kind_cache.insert(ty, result); @@ -1459,7 +1433,7 @@ fn is_instantiable(cx: ctxt, r_ty: t) -> bool { ty_fn(_) | ty_var(_) | ty_param(_, _) | - ty_self(_) | + ty_self | ty_type | ty_opaque_box | ty_opaque_closure_ptr(_) | @@ -1850,9 +1824,7 @@ fn hash_type_structure(st: sty) -> uint { } ty_var(v) { hash_uint(30u, v.to_uint()) } ty_param(pid, did) { hash_def(hash_uint(31u, pid), did) } - ty_self(substs) { - hash_substs(28u, substs) - } + ty_self { 28u } ty_type { 32u } ty_bot { 34u } ty_ptr(mt) { hash_subty(35u, mt.ty) } @@ -2081,7 +2053,7 @@ fn sort_methods(meths: [method]) -> [method] { fn occurs_check(tcx: ctxt, sp: span, vid: ty_vid, rt: t) { // Fast path - if !type_has_vars(rt) { ret; } + if !type_needs_infer(rt) { ret; } // Occurs check! if vec::contains(vars_in_type(rt), vid) { @@ -2190,7 +2162,7 @@ fn ty_sort_str(cx: ctxt, t: t) -> str { ty_tup(_) { "tuple" } ty_var(_) { "variable" } ty_param(_, _) { "type parameter" } - ty_self(_) { "self" } + ty_self { "self" } ty_constr(t, _) { ty_sort_str(cx, t) } } } @@ -2274,6 +2246,9 @@ fn type_err_to_str(cx: ctxt, err: type_err) -> str { terr_sorts(exp, act) { ret #fmt("%s vs %s", ty_sort_str(cx, exp), ty_sort_str(cx, act)); } + terr_self_substs { + ret "inconsistent self substitution"; // XXX this is more of a bug + } } } @@ -2730,7 +2705,9 @@ fn normalize_ty(cx: ctxt, t: t) -> t { alt r.self_r { some(_) { // This enum has a self region. Get rid of it - mk_enum(cx, did, {self_r: none, tps: r.tps }) + mk_enum(cx, did, {self_r: none, + self_ty: none, + tps: r.tps}) } none { t } } diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index 7ff451a6658..6023d830229 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -272,7 +272,7 @@ fn instantiate_path(fcx: @fn_ctxt, pth.types.map { |aty| fcx.to_ty(aty) } }; - let substs = {self_r: self_r, tps: tps}; + let substs = {self_r: self_r, self_ty: none, tps: tps}; fcx.write_ty_substs(id, tpt.ty, substs); } @@ -340,6 +340,8 @@ iface ast_conv { fn tcx() -> ty::ctxt; fn ccx() -> @crate_ctxt; fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty; + + // what type should we use when a type is omitted? fn ty_infer(span: span) -> ty::t; } @@ -536,7 +538,7 @@ fn ast_path_to_substs_and_ty( } let tps = path.types.map { |a_t| ast_ty_to_ty(self, rscope, a_t) }; - let substs = {self_r: self_r, tps: tps}; + let substs = {self_r:self_r, self_ty:none, tps:tps}; {substs: substs, ty: ty::subst(tcx, substs, decl_ty)} } @@ -569,9 +571,11 @@ fn instantiate_iface_ref(ccx: @crate_ctxt, t: @ast::iface_ref, let sp = t.path.span, err = "can only implement interface types", sess = ccx.tcx.sess; + let rscope = type_rscope(rp); + alt lookup_def_tcx(ccx.tcx, t.path.span, t.id) { ast::def_ty(t_id) { - let tpt = ast_path_to_ty(ccx, type_rscope(rp), t_id, t.path, t.id); + let tpt = ast_path_to_ty(ccx, rscope, t_id, t.path, t.id); alt ty::get(tpt.ty).struct { ty::ty_iface(*) { (t_id, tpt) @@ -740,11 +744,11 @@ fn ast_ty_to_ty( check_path_args(tcx, path, NO_TPS | NO_REGIONS); ty::mk_param(tcx, n, id) } - ast::def_self(self_id) { - let {substs, ty: _} = - ast_path_to_substs_and_ty(self, rscope, - local_def(self_id), path); - ty::mk_self(tcx, substs) + ast::def_self(_) { + // n.b.: resolve guarantees that the self type only appears in an + // iface, which we rely upon in various places when creating + // substs + ty::mk_self(tcx) } _ { tcx.sess.span_fatal(ast_ty.span, @@ -1137,30 +1141,35 @@ fn ty_of_native_fn_decl(ccx: @crate_ctxt, fn ty_param_bounds(ccx: @crate_ctxt, params: [ast::ty_param]) -> @[ty::param_bounds] { + fn compute_bounds(ccx: @crate_ctxt, + param: ast::ty_param) -> ty::param_bounds { + @vec::flat_map(*param.bounds) { |b| + alt b { + ast::bound_send { [ty::bound_send] } + ast::bound_copy { [ty::bound_copy] } + ast::bound_iface(t) { + let ity = ast_ty_to_ty(ccx, empty_rscope, t); + alt ty::get(ity).struct { + ty::ty_iface(*) { + [ty::bound_iface(ity)] + } + _ { + ccx.tcx.sess.span_err( + t.span, "type parameter bounds must be \ + interface types"); + [] + } + } + } + } + } + } + @params.map { |param| alt ccx.tcx.ty_param_bounds.find(param.id) { some(bs) { bs } none { - let bounds = @vec::flat_map(*param.bounds) { |b| - alt b { - ast::bound_send { [ty::bound_send] } - ast::bound_copy { [ty::bound_copy] } - ast::bound_iface(t) { - let ity = ccx.to_ty(empty_rscope, t); - alt ty::get(ity).struct { - ty::ty_iface(_, _) { - [ty::bound_iface(ity)] - } - _ { - ccx.tcx.sess.span_err( - t.span, "type parameter bounds must be \ - interface types"); - [] - } - } - } - } - }; + let bounds = compute_bounds(ccx, param); ccx.tcx.ty_param_bounds.insert(param.id, bounds); bounds } @@ -1354,7 +1363,7 @@ fn mk_substs(ccx: @crate_ctxt, atps: [ast::ty_param], rp: ast::region_param) ast::rp_self { some(ty::re_bound(ty::br_self)) } ast::rp_none { none } }; - {bounds: bounds, substs: {self_r: self_r, tps: params}} + {bounds: bounds, substs: {self_r: self_r, self_ty: none, tps: params}} } fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method, @@ -1375,7 +1384,7 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method, } else { let auto_modes = vec::map2(impl_m.fty.inputs, if_m.fty.inputs, {|i, f| alt ty::get(f.ty).struct { - ty::ty_param(_, _) | ty::ty_self(_) + ty::ty_param(*) | ty::ty_self if alt i.mode { ast::infer(_) { true } _ { false } } { {mode: ast::expl(ast::by_ref) with i} } @@ -1387,13 +1396,13 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method, // Add dummy substs for the parameters of the impl method let substs = { self_r: substs.self_r, + self_ty: some(self_ty), tps: substs.tps + vec::from_fn(vec::len(*if_m.tps), {|i| ty::mk_param(tcx, i + impl_tps, {crate: 0, node: 0}) }) }; let mut if_fty = ty::mk_fn(tcx, if_m.fty); if_fty = ty::subst(tcx, substs, if_fty); - if_fty = fixup_self_full(tcx, if_fty, substs, self_ty, impl_tps); require_same_types( tcx, sp, impl_fty, if_fty, {|| "method `" + if_m.ident + @@ -1402,104 +1411,6 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method, } } -// Mangles an iface method ty to make its self type conform to the self type -// of a specific impl or bounded type parameter. This is rather involved -// because the type parameters of ifaces and impls are not required to line up -// (an impl can have less or more parameters than the iface it implements), so -// some mangling of the substituted types is required. -fn fixup_self_full(cx: ty::ctxt, mty: ty::t, m_substs: ty::substs, - selfty: ty::t, impl_n_tps: uint) -> ty::t { - - if !ty::type_has_vars(mty) { ret mty; } - - ty::fold_ty(cx, mty) {|t| - alt ty::get(t).struct { - ty::ty_self(substs) if ty::substs_is_noop(substs) { - selfty - } - ty::ty_self(substs) { - // Move the substs into the type param system of the - // context. - let mut substs_tps = vec::map(substs.tps) {|t| - let f = fixup_self_full(cx, t, m_substs, selfty, impl_n_tps); - ty::subst(cx, m_substs, f) - }; - - // Add extra substs for impl type parameters. - while vec::len(substs_tps) < impl_n_tps { - substs_tps += [ty::mk_param(cx, substs_tps.len(), - {crate: 0, node: 0})]; - } - - // And for method type parameters. - let method_n_tps = ( - m_substs.tps.len() - substs_tps.len()) as int; - if method_n_tps > 0 { - substs_tps += vec::tailn( - m_substs.tps, - m_substs.tps.len() - (method_n_tps as uint)); - } - - // And then instantiate the self type using all those. - let substs_1 = { - self_r: substs.self_r, - tps: substs_tps - }; - ty::subst(cx, substs_1, selfty) - } - _ { - t - } - } - } -} - -// Mangles an iface method ty to make its self type conform to the self type -// of a specific impl or bounded type parameter. This is rather involved -// because the type parameters of ifaces and impls are not required to line up -// (an impl can have less or more parameters than the iface it implements), so -// some mangling of the substituted types is required. -fn fixup_self_param(fcx: @fn_ctxt, mty: ty::t, m_substs: ty::substs, - selfty: ty::t, sp: span) -> ty::t { - if !ty::type_has_vars(mty) { ret mty; } - - let tcx = fcx.ccx.tcx; - ty::fold_ty(tcx, mty) {|t| - alt ty::get(t).struct { - ty::ty_self(substs) if ty::substs_is_noop(substs) { - selfty - } - ty::ty_self(substs) { - // Move the substs into the type param system of the context. - let tps_p = vec::map(substs.tps) {|t| - let f = fixup_self_param(fcx, t, m_substs, selfty, sp); - ty::subst(tcx, m_substs, f) - }; - let substs_p = {self_r: substs.self_r, tps: tps_p}; - let self_p = ty::mk_self(tcx, substs_p); - let m_self = ty::mk_self(tcx, m_substs); - demand::suptype(fcx, sp, m_self, self_p); - selfty - } - _ { t } - } - } -} - -// Replaces all occurrences of the `self` region with `with_region`. Note -// that we descend into `fn()` types here, because `fn()` does not bind the -// `self` region. -fn replace_self_region(tcx: ty::ctxt, with_region: ty::region, - ty: ty::t) -> ty::t { - - ty::fold_region(tcx, ty) {|r, _under_rptr| - alt r { - ty::re_bound(re_self) { with_region } - _ { r } - } - } -} - // Item collection - a pair of bootstrap passes: // // (1) Collect the IDs of all type items (typedefs) and store them in a table. @@ -1932,7 +1843,7 @@ mod writeback { fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t) -> option { - if !ty::type_has_vars(typ) { ret some(typ); } + if !ty::type_needs_infer(typ) { ret some(typ); } alt infer::resolve_deep(fcx.infcx, typ, true) { result::ok(new_type) { ret some(new_type); } result::err(e) { @@ -2571,7 +2482,8 @@ fn impl_self_ty(fcx: @fn_ctxt, did: ast::def_id) -> ty_param_substs_and_ty { {self_r: alt rp { ast::rp_self { some(fcx.next_region_var()) } ast::rp_none { none }}, - tps: ty::ty_params_to_tys(tcx, ts)})} + self_ty: none, + tps: ty::ty_params_to_tys(tcx, ts)})} } _ { tcx.sess.bug("impl_self_ty: unbound item or item that \ doesn't have a self_ty"); } @@ -2589,7 +2501,7 @@ fn impl_self_ty(fcx: @fn_ctxt, did: ast::def_id) -> ty_param_substs_and_ty { }; let tps = fcx.next_ty_vars(n_tps); - let substs = {self_r: self_r, tps: tps}; + let substs = {self_r: self_r, self_ty: none, tps: tps}; let substd_ty = ty::subst(tcx, substs, raw_ty); {substs: substs, ty: substd_ty} } @@ -2657,8 +2569,18 @@ impl methods for lookup { } some(pos) { + // Replace any appearance of `self` with the type of the + // generic parameter itself. Note that this is the only case + // where this replacement is necessary: in all other cases, we + // are either invoking a method directly from an impl or class + // (where the self type is not permitted), or from a iface + // type (in which case methods that refer to self are not + // permitted). + let substs = {self_ty: some(self.self_ty) + with bound_substs}; + ret some(self.write_mty_from_m( - some(self.self_ty), bound_substs, ifce_methods[pos], + substs, ifce_methods[pos], method_param(iid, pos, n, iface_bnd_idx))); } } @@ -2675,23 +2597,28 @@ impl methods for lookup { let m_fty = ty::mk_fn(self.tcx(), m.fty); - if ty::type_has_vars(m_fty) { - self.tcx().sess.span_fatal( + if ty::type_has_self(m_fty) { + self.tcx().sess.span_err( self.expr.span, "can not call a method that contains a \ self type through a boxed iface"); } if (*m.tps).len() > 0u { - self.tcx().sess.span_fatal( + self.tcx().sess.span_err( self.expr.span, "can not call a generic method through a \ boxed iface"); } + // Note: although it is illegal to invoke a method that uses self + // through a iface instance, we use a dummy subst here so that we + // can soldier on with the compilation. + let substs = {self_ty: some(self.self_ty) + with iface_substs}; + ret some(self.write_mty_from_m( - none, iface_substs, m, - method_iface(did, i))); + substs, m, method_iface(did, i))); } ret none; @@ -2717,7 +2644,7 @@ impl methods for lookup { self.tcx(), did, self.m_name, self.expr.span); ret some(self.write_mty_from_m( - none, class_substs, m, + class_substs, m, method_static(m_declared))); } @@ -2813,7 +2740,7 @@ impl methods for lookup { let (self_substs, n_tps, did) = results[0]; let fty = self.ty_from_did(did); ret some(self.write_mty_from_fty( - none, self_substs, n_tps, fty, + self_substs, n_tps, fty, method_static(did))); } } @@ -2821,8 +2748,7 @@ impl methods for lookup { ret none; } - fn write_mty_from_m(self_ty_sub: option, - self_substs: ty::substs, + fn write_mty_from_m(self_substs: ty::substs, m: ty::method, origin: method_origin) -> method_origin { let tcx = self.fcx.ccx.tcx; @@ -2831,18 +2757,16 @@ impl methods for lookup { // a.b has a protocol like fn@() (perhaps eventually fn&()): let fty = ty::mk_fn(tcx, {proto: ast::proto_box with m.fty}); - ret self.write_mty_from_fty(self_ty_sub, self_substs, - (*m.tps).len(), fty, origin); + ret self.write_mty_from_fty(self_substs, (*m.tps).len(), + fty, origin); } - fn write_mty_from_fty(self_ty_sub: option, - self_substs: ty::substs, + fn write_mty_from_fty(self_substs: ty::substs, n_tps_m: uint, fty: ty::t, origin: method_origin) -> method_origin { let tcx = self.fcx.ccx.tcx; - let has_self = ty::type_has_vars(fty); // Here I will use the "c_" prefix to refer to the method's // owner. You can read it as class, but it may also be an iface. @@ -2867,26 +2791,11 @@ impl methods for lookup { } }; - let all_substs = {self_r: self_substs.self_r, - tps: self_substs.tps + m_substs}; + let all_substs = {tps: self_substs.tps + m_substs + with self_substs}; + self.fcx.write_ty_substs(self.node_id, fty, all_substs); - // n.b. This treatment of self is risky but ok. As a rule of thumb, - // one ought to substitute all type parameters at once, and we are not - // doing so here. The danger you open up has to do with the - // possibility that one of the substs in `all_substs` maps to a self - // type. However, right now I think it is safe because the types in - // `all_substs` may not refer to self. This may not stay true - // forever, though. - - if has_self && !option::is_none(self_ty_sub) { - let fty = self.fcx.node_ty(self.node_id); - let fty = fixup_self_param( - self.fcx, fty, all_substs, self_ty_sub.get(), - self.expr.span); - self.fcx.write_ty(self.node_id, fty); - } - ret origin; } } @@ -4697,13 +4606,13 @@ mod vtable { } fn fixup_substs(fcx: @fn_ctxt, sp: span, - substs: ty::substs) -> ty::substs { + id: ast::def_id, substs: ty::substs) -> ty::substs { let tcx = fcx.ccx.tcx; // use a dummy type just to package up the substs that need fixing up - let t = ty::mk_self(tcx, substs); + let t = ty::mk_iface(tcx, id, substs); let t_f = fixup_ty(fcx, sp, t); alt check ty::get(t_f).struct { - ty::ty_self(substs_f) { substs_f } + ty::ty_iface(_, substs_f) { substs_f } } } @@ -4754,7 +4663,7 @@ mod vtable { relate_iface_tys(fcx, sp, iface_ty, ty); if !allow_unsafe { for vec::each(*ty::iface_methods(tcx, did)) {|m| - if ty::type_has_vars(ty::mk_fn(tcx, m.fty)) { + if ty::type_has_self(ty::mk_fn(tcx, m.fty)) { tcx.sess.span_err( sp, "a boxed iface with self types may not be \ passed as a bounded type"); @@ -4804,7 +4713,7 @@ mod vtable { // recursively process the bounds let iface_tps = iface_substs.tps; - let substs_f = fixup_substs(fcx, sp, substs); + let substs_f = fixup_substs(fcx, sp, iface_id, substs); connect_iface_tps(fcx, sp, substs_f.tps, iface_tps, im.did); let subres = lookup_vtables(fcx, isc, sp, diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs index d5758605d63..08455b5104a 100644 --- a/src/rustc/util/ppaux.rs +++ b/src/rustc/util/ppaux.rs @@ -97,7 +97,7 @@ fn ty_to_str(cx: ctxt, typ: t) -> str { let modestr = alt canon_mode(cx, mode) { ast::infer(_) { "" } ast::expl(m) { - if !ty::type_has_vars(ty) && + if !ty::type_needs_infer(ty) && m == ty::default_arg_mode_for_ty(ty) { "" } else { @@ -185,9 +185,7 @@ fn ty_to_str(cx: ctxt, typ: t) -> str { ty_param(id, _) { "'" + str::from_bytes([('a' as u8) + (id as u8)]) } - ty_self(substs) { - parameterized(cx, "self", substs.self_r, substs.tps) - } + ty_self { "self" } ty_enum(did, substs) | ty_res(did, _, substs) | ty_class(did, substs) { let path = ty::item_path(cx, did); let base = ast_map::path_to_str(path); diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs index 8559d78bcdf..8bd0a2eb614 100644 --- a/src/test/compile-fail/issue-2149.rs +++ b/src/test/compile-fail/issue-2149.rs @@ -1,7 +1,4 @@ -iface monad { - fn bind(fn(A) -> self); -} -impl monad of monad for [A] { +impl monad for [A] { fn bind(f: fn(A) -> [B]) { let mut r = fail; for self.each {|elt| r += f(elt); } diff --git a/src/test/compile-fail/selftype-astparam.rs b/src/test/compile-fail/selftype-astparam.rs new file mode 100644 index 00000000000..b4c6cf5c82d --- /dev/null +++ b/src/test/compile-fail/selftype-astparam.rs @@ -0,0 +1,15 @@ +iface add { + fn +(x: self) -> self; +} + +impl of add for int { + fn +(x: int) -> int { self + x } +} + +fn do_add(x: A, y: A) -> A { x + y } + +fn main() { + let x = 3 as add; + let y = 4 as add; + do_add(x, y); //! ERROR a boxed iface with self types may not be passed as a bounded type +} diff --git a/src/test/compile-fail/selftype-ifacetype.rs b/src/test/compile-fail/selftype-ifacetype.rs new file mode 100644 index 00000000000..2772b359849 --- /dev/null +++ b/src/test/compile-fail/selftype-ifacetype.rs @@ -0,0 +1,9 @@ +iface add { + fn +(x: self) -> self; +} + +fn do_add(x: add, y: add) -> add { + x + y //! ERROR can not call a method that contains a self type through a boxed iface +} + +fn main() {} diff --git a/src/test/run-pass/monad.rs b/src/test/run-pass/monad.rs index 8779cef0470..d14de311d9e 100644 --- a/src/test/run-pass/monad.rs +++ b/src/test/run-pass/monad.rs @@ -1,8 +1,4 @@ -iface monad { - fn bind(fn(A) -> self) -> self; -} - -impl of monad for [A] { +impl monad for [A] { fn bind(f: fn(A) -> [B]) -> [B] { let mut r = []; for self.each {|elt| r += f(elt); } @@ -10,7 +6,7 @@ impl of monad for [A] { } } -impl of monad for option { +impl monad for option { fn bind(f: fn(A) -> option) -> option { alt self { some(a) { f(a) } diff --git a/src/test/run-pass/selftype-add-ints.rs b/src/test/run-pass/selftype-add-ints.rs new file mode 100644 index 00000000000..dabb700da35 --- /dev/null +++ b/src/test/run-pass/selftype-add-ints.rs @@ -0,0 +1,19 @@ +iface add { + fn +(x: self) -> self; +} + +impl of add for int { + fn +(x: int) -> int { self + x } +} + +impl of add for @const int { + fn +(x: @const int) -> @const int { @(*self + *x) } +} + +fn do_add(+x: A, +y: A) -> A { x + y } + +fn main() { + assert do_add(3, 4) == 7; + assert do_add(@3, @4) == @7; + assert do_add(@mut 3, @mut 4) == @mut 7; +}