diff --git a/src/libcore/io.rs b/src/libcore/io.rs index aa2eccfb343d8..1bebf817f9bbd 100644 --- a/src/libcore/io.rs +++ b/src/libcore/io.rs @@ -1047,7 +1047,7 @@ pub mod fsync { option::None => (), option::Some(level) => { // fail hard if not succesful - assert(self.arg.fsync_fn(self.arg.val, level) != -1); + assert((self.arg.fsync_fn)(self.arg.val, level) != -1); } } } diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs index fa364700b74d3..ea86db77c3f45 100644 --- a/src/librustc/lib/llvm.rs +++ b/src/librustc/lib/llvm.rs @@ -312,7 +312,7 @@ extern mod llvm { fn LLVMStructType(ElementTypes: *TypeRef, ElementCount: c_uint, Packed: Bool) -> TypeRef; fn LLVMCountStructElementTypes(StructTy: TypeRef) -> c_uint; - fn LLVMGetStructElementTypes(StructTy: TypeRef, Dest: *TypeRef); + fn LLVMGetStructElementTypes(StructTy: TypeRef, Dest: *mut TypeRef); fn LLVMIsPackedStruct(StructTy: TypeRef) -> Bool; /* Operations on array, pointer, and vector types (sequence types) */ @@ -1123,10 +1123,9 @@ fn type_to_str_inner(names: type_names, outer0: ~[TypeRef], ty: TypeRef) -> Struct => { let mut s: ~str = ~"{"; let n_elts = llvm::LLVMCountStructElementTypes(ty) as uint; - let elts = vec::from_elem(n_elts, 0 as TypeRef); - unsafe { - llvm::LLVMGetStructElementTypes(ty, vec::raw::to_ptr(elts)); - } + let mut elts = vec::from_elem(n_elts, 0 as TypeRef); + llvm::LLVMGetStructElementTypes(ty, + ptr::to_mut_unsafe_ptr(&mut elts[0])); s += tys_str(names, outer, elts); s += ~"}"; return s; @@ -1179,6 +1178,18 @@ fn fn_ty_param_tys(fn_ty: TypeRef) -> ~[TypeRef] unsafe { return args; } +fn struct_element_types(struct_ty: TypeRef) -> ~[TypeRef] { + unsafe { + let count = llvm::LLVMCountStructElementTypes(struct_ty); + let mut buf: ~[TypeRef] = + vec::from_elem(count as uint, + cast::transmute::(0)); + llvm::LLVMGetStructElementTypes(struct_ty, + ptr::to_mut_unsafe_ptr(&mut buf[0])); + return move buf; + } +} + /* Memory-managed interface to target data. */ diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index a9deb51f1c71e..7cf5ad32b3c77 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -646,6 +646,9 @@ fn check_loans_in_expr(expr: @ast::expr, ast::expr_call(f, args, _) => { self.check_call(expr, Some(f), f.id, f.span, args); } + ast::expr_method_call(_, _, _, args, _) => { + self.check_call(expr, None, expr.callee_id, expr.span, args); + } ast::expr_index(_, rval) | ast::expr_binary(_, _, rval) if self.bccx.method_map.contains_key(expr.id) => { diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs index 25c43019e47bb..b7c9c3963334d 100644 --- a/src/librustc/middle/borrowck/gather_loans.rs +++ b/src/librustc/middle/borrowck/gather_loans.rs @@ -125,6 +125,25 @@ fn req_loans_in_expr(ex: @ast::expr, visit::visit_expr(ex, self, vt); } + ast::expr_method_call(_, _, _, args, _) => { + let arg_tys = ty::ty_fn_args(ty::node_id_to_type(self.tcx(), + ex.callee_id)); + let scope_r = ty::re_scope(ex.id); + for vec::each2(args, arg_tys) |arg, arg_ty| { + match ty::resolved_mode(self.tcx(), arg_ty.mode) { + ast::by_ref => { + let arg_cmt = self.bccx.cat_expr(*arg); + self.guarantee_valid(arg_cmt, m_imm, scope_r); + } + ast::by_val | ast::by_move | ast::by_copy => {} + } + } + + // XXX: Do something with the method receiver? + + visit::visit_expr(ex, self, vt); + } + ast::expr_match(ex_v, arms) => { let cmt = self.bccx.cat_expr(ex_v); for arms.each |arm| { diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index e900b13b7c5e5..77197a1db0d58 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -94,19 +94,37 @@ fn check_expr(sess: Session, def_map: resolve::DefMap, } match def_map.find(e.id) { Some(def_const(def_id)) | - Some(def_fn(def_id, _)) => { + Some(def_fn(def_id, _)) | + Some(def_variant(_, def_id)) | + Some(def_class(def_id)) => { if !ast_util::is_local(def_id) { sess.span_err( e.span, ~"paths in constants may only refer to \ - crate-local constants or functions"); + crate-local constants, functions, or \ + structs"); } } - _ => { + Some(def) => { + debug!("(checking const) found bad def: %?", def); sess.span_err( e.span, - ~"paths in constants may only refer to \ - constants or functions"); + fmt!("paths in constants may only refer to \ + constants or functions")); } + None => { + sess.span_bug(e.span, ~"unbound path in const?!"); + } + } + } + expr_call(callee, _, false) => { + match def_map.find(callee.id) { + Some(def_class(*)) => {} // OK. + _ => { + sess.span_err( + e.span, + ~"function calls in constants are limited to \ + structure constructors"); + } } } expr_paren(e) => { check_expr(sess, def_map, method_map, diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index c0887150f24f2..b4099325fb1bb 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -339,6 +339,18 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt) { } } } + expr_method_call(_, _, _, args, _) => { + for ty::ty_fn_args(ty::node_id_to_type(cx.tcx, e.callee_id)).eachi + |i, arg_t| { + match ty::arg_mode(cx.tcx, *arg_t) { + by_copy => maybe_copy(cx, args[i], + Some(("function arguments must be copyable", + "try changing the function to take a \ + reference instead"))), + by_ref | by_val | by_move => () + } + } + } expr_field(lhs, _, _) => { // If this is a method call with a by-val argument, we need // to check the copy diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 86136d60d5420..985f62e86a610 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -550,8 +550,8 @@ fn visit_expr(expr: @expr, &&self: @IrMaps, vt: vt<@IrMaps>) { // otherwise, live nodes are not required: expr_index(*) | expr_field(*) | expr_vstore(*) | - expr_vec(*) | expr_rec(*) | expr_call(*) | expr_tup(*) | - expr_log(*) | expr_binary(*) | + expr_vec(*) | expr_rec(*) | expr_call(*) | expr_method_call(*) | + expr_tup(*) | expr_log(*) | expr_binary(*) | expr_assert(*) | expr_addr_of(*) | expr_copy(*) | expr_loop_body(*) | expr_do_body(*) | expr_cast(*) | expr_unary(*) | expr_fail(*) | @@ -1237,6 +1237,17 @@ impl Liveness { self.propagate_through_expr(f, succ) } + expr_method_call(rcvr, _, _, args, _) => { + // calling a method with bot return type means that the method + // will fail, and hence the successors can be ignored + let t_ret = ty::ty_fn_ret(ty::node_id_to_type(self.tcx, + expr.callee_id)); + let succ = if ty::type_is_bot(t_ret) {self.s.exit_ln} + else {succ}; + let succ = self.propagate_through_exprs(args, succ); + self.propagate_through_expr(rcvr, succ) + } + expr_tup(exprs) => { self.propagate_through_exprs(exprs, succ) } @@ -1486,6 +1497,26 @@ fn check_arm(arm: arm, &&self: @Liveness, vt: vt<@Liveness>) { visit::visit_arm(arm, self, vt); } +fn check_call(args: &[@expr], + targs: &[ty::arg], + &&self: @Liveness) { + for vec::each2(args, targs) |arg_expr, arg_ty| { + match ty::resolved_mode(self.tcx, arg_ty.mode) { + by_val | by_copy | by_ref => {} + by_move => { + if ty::expr_is_lval(self.tcx, self.ir.method_map, *arg_expr) { + // Probably a bad error message (what's an rvalue?) + // but I can't think of anything better + self.tcx.sess.span_err(arg_expr.span, + fmt!("move mode argument must be an rvalue: try (move \ + %s) instead", + expr_to_str(*arg_expr, self.tcx.sess.intr()))); + } + } + } + } +} + fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) { match expr.node { expr_path(_) => { @@ -1531,23 +1562,14 @@ fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) { expr_call(f, args, _) => { let targs = ty::ty_fn_args(ty::expr_ty(self.tcx, f)); - for vec::each2(args, targs) |arg_expr, arg_ty| { - match ty::resolved_mode(self.tcx, arg_ty.mode) { - by_val | by_copy | by_ref => {} - by_move => { - if ty::expr_is_lval(self.tcx, self.ir.method_map, - *arg_expr) { - // Probably a bad error message (what's an rvalue?) - // but I can't think of anything better - self.tcx.sess.span_err(arg_expr.span, - fmt!("Move mode argument must be an rvalue: try \ - (move %s) instead", expr_to_str(*arg_expr, - self.tcx.sess.intr()))); - } - } - } - } + check_call(args, targs, self); + visit::visit_expr(expr, self, vt); + } + expr_method_call(_, _, _, args, _) => { + let targs = ty::ty_fn_args(ty::node_id_to_type(self.tcx, + expr.callee_id)); + check_call(args, targs, self); visit::visit_expr(expr, self, vt); } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index fd5735e78deff..e42d18a06c973 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -530,7 +530,8 @@ impl &mem_categorization_ctxt { ast::expr_swap(*) | ast::expr_assign(*) | ast::expr_assign_op(*) | ast::expr_fn(*) | ast::expr_fn_block(*) | ast::expr_assert(*) | ast::expr_ret(*) | - ast::expr_loop_body(*) | ast::expr_do_body(*) | ast::expr_unary(*) | + ast::expr_loop_body(*) | ast::expr_do_body(*) | + ast::expr_unary(*) | ast::expr_method_call(*) | ast::expr_copy(*) | ast::expr_cast(*) | ast::expr_fail(*) | ast::expr_vstore(*) | ast::expr_vec(*) | ast::expr_tup(*) | ast::expr_if(*) | ast::expr_log(*) | diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index fbbef2a3658ab..06011d928fd72 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -258,7 +258,7 @@ fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt) { // FIXME #3387 // ast::expr_index(*) | ast::expr_binary(*) | // ast::expr_unary(*) | - ast::expr_call(*) => { + ast::expr_call(*) | ast::expr_method_call(*) => { debug!("node %d: %s", expr.id, pprust::expr_to_str(expr, cx.sess.intr())); new_cx.parent = Some(expr.id); diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index c9ebf55b57cf4..76948fee5d868 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -16,12 +16,11 @@ use syntax::ast::{def_foreign_mod, def_id, def_label, def_local, def_mod}; use syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param}; use syntax::ast::{def_typaram_binder, def_static_method}; use syntax::ast::{def_upvar, def_use, def_variant, expr, expr_assign_op}; -use syntax::ast::{expr_binary, expr_cast, expr_field, expr_fn}; -use syntax::ast::{expr_fn_block, expr_index, expr_path}; +use syntax::ast::{expr_binary, expr_break, expr_cast, expr_field, expr_fn}; +use syntax::ast::{expr_fn_block, expr_index, expr_method_call, expr_path}; use syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param}; use syntax::ast::{def_upvar, def_use, def_variant, div, eq}; use syntax::ast::{enum_variant_kind, expr, expr_again, expr_assign_op}; -use syntax::ast::{expr_binary, expr_break, expr_cast, expr_field, expr_fn}; use syntax::ast::{expr_fn_block, expr_index, expr_loop}; use syntax::ast::{expr_path, expr_struct, expr_unary, fn_decl}; use syntax::ast::{foreign_item, foreign_item_const, foreign_item_fn, ge}; @@ -5008,6 +5007,10 @@ impl Resolver { let traits = self.search_for_traits_containing_method(ident); self.trait_map.insert(expr.id, traits); } + expr_method_call(_, ident, _, _, _) => { + let traits = self.search_for_traits_containing_method(ident); + self.trait_map.insert(expr.id, traits); + } expr_binary(add, _, _) | expr_assign_op(add, _, _) => { self.add_fixed_trait_for_expr(expr.id, self.lang_items.add_trait); diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index 796b426353565..b3b4238034a8c 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -292,6 +292,38 @@ fn trans_call(in_cx: block, |cx| trans(cx, f), args, dest, DontAutorefArg) } +fn trans_method_call(in_cx: block, + call_ex: @ast::expr, + rcvr: @ast::expr, + args: CallArgs, + dest: expr::Dest) + -> block { + let _icx = in_cx.insn_ctxt("trans_method_call"); + trans_call_inner( + in_cx, + call_ex.info(), + node_id_type(in_cx, call_ex.callee_id), + expr_ty(in_cx, call_ex), + |cx| { + match cx.ccx().maps.method_map.find(call_ex.id) { + Some(origin) => { + meth::trans_method_callee(cx, + call_ex.callee_id, + rcvr, + origin) + } + None => { + cx.tcx().sess.span_bug(call_ex.span, + ~"method call expr wasn't in \ + method map") + } + } + }, + args, + dest, + DontAutorefArg) +} + fn trans_rtcall(bcx: block, name: ~str, args: ~[ValueRef], dest: expr::Dest) -> block { diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 73d6741feea79..607d797048b48 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -581,8 +581,9 @@ fn val_str(tn: type_names, v: ValueRef) -> ~str { fn struct_elt(llstructty: TypeRef, n: uint) -> TypeRef unsafe { let elt_count = llvm::LLVMCountStructElementTypes(llstructty) as uint; assert (n < elt_count); - let elt_tys = vec::from_elem(elt_count, T_nil()); - llvm::LLVMGetStructElementTypes(llstructty, to_ptr(elt_tys)); + let mut elt_tys = vec::from_elem(elt_count, T_nil()); + llvm::LLVMGetStructElementTypes(llstructty, + ptr::to_mut_unsafe_ptr(&mut elt_tys[0])); return llvm::LLVMGetElementType(elt_tys[n]); } @@ -822,11 +823,12 @@ fn T_task(targ_cfg: @session::config) -> TypeRef { fn T_tydesc_field(cx: @crate_ctxt, field: uint) -> TypeRef unsafe { // Bit of a kludge: pick the fn typeref out of the tydesc.. - let tydesc_elts: ~[TypeRef] = + let mut tydesc_elts: ~[TypeRef] = vec::from_elem::(abi::n_tydesc_fields, T_nil()); - llvm::LLVMGetStructElementTypes(cx.tydesc_type, - to_ptr::(tydesc_elts)); + llvm::LLVMGetStructElementTypes( + cx.tydesc_type, + ptr::to_mut_unsafe_ptr(&mut tydesc_elts[0])); let t = llvm::LLVMGetElementType(tydesc_elts[field]); return t; } diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index dcf5cee952f29..616ad48d26efb 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -369,18 +369,62 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { ast::expr_path(pth) => { assert pth.types.len() == 0; match cx.tcx.def_map.find(e.id) { - Some(ast::def_fn(def_id, _)) => { - assert ast_util::is_local(def_id); - let f = base::get_item_val(cx, def_id.node); - C_struct(~[f, C_null(T_opaque_box_ptr(cx))]) - } - Some(ast::def_const(def_id)) => { - get_const_val(cx, def_id) - } - _ => cx.sess.span_bug(e.span, ~"expected a const or fn def") + Some(ast::def_fn(def_id, _)) => { + assert ast_util::is_local(def_id); + let f = base::get_item_val(cx, def_id.node); + C_struct(~[f, C_null(T_opaque_box_ptr(cx))]) + } + Some(ast::def_const(def_id)) => { + get_const_val(cx, def_id) + } + Some(ast::def_variant(enum_did, variant_did)) => { + let ety = ty::expr_ty(cx.tcx, e); + let llty = type_of::type_of(cx, ety); + let llstructtys = lib::llvm::struct_element_types(llty); + + // Can't use `discrims` from the crate context here because + // those discriminants have an extra level of indirection, + // and there's no LLVM constant load instruction. + let mut lldiscrim = None; + for ty::enum_variants(cx.tcx, enum_did).each |variant_info| { + if variant_info.id == variant_did { + lldiscrim = Some(C_int(cx, variant_info.disr_val)); + break; + } + } + + C_named_struct( + llty, + ~[ lldiscrim.get(), C_null(llstructtys[1]) ] + ) + } + Some(ast::def_class(_)) => { + let ety = ty::expr_ty(cx.tcx, e); + let llty = type_of::type_of(cx, ety); + C_null(llty) + } + _ => { + cx.sess.span_bug(e.span, + ~"expected a const, fn, or variant def") + } + } + } + ast::expr_call(callee, args, _) => { + match cx.tcx.def_map.find(callee.id) { + Some(ast::def_class(def_id)) => { + let ety = ty::expr_ty(cx.tcx, e); + let llty = type_of::type_of(cx, ety); + let llstructbody = C_struct(args.map(|a| const_expr(cx, *a))); + if ty::ty_dtor(cx.tcx, def_id).is_present() { + C_named_struct(llty, ~[ llstructbody, C_u8(0) ]) + } else { + C_named_struct(llty, ~[ llstructbody ]) + } + } + _ => cx.sess.span_bug(e.span, ~"expected a struct def") } } - ast::expr_paren(e) => { return const_expr(cx, e); } + ast::expr_paren(e) => { return const_expr(cx, e); } _ => cx.sess.span_bug(e.span, ~"bad constant expression type in consts::const_expr") }; diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 36696594e423c..2cd68bd0d2326 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -592,6 +592,13 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr, return callee::trans_call( bcx, expr, f, callee::ArgExprs(args), expr.id, dest); } + ast::expr_method_call(rcvr, _, _, args, _) => { + return callee::trans_method_call(bcx, + expr, + rcvr, + callee::ArgExprs(args), + dest); + } ast::expr_binary(_, lhs, rhs) => { // if not overloaded, would be RvalueDatumExpr return trans_overloaded_op(bcx, expr, lhs, ~[rhs], dest, diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index 4fe7c307d5977..6ff11b0914630 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -75,10 +75,9 @@ fn classify_ty(ty: TypeRef) -> ~[x86_64_reg_class] { fn struct_tys(ty: TypeRef) -> ~[TypeRef] { let n = llvm::LLVMCountStructElementTypes(ty); - let elts = vec::from_elem(n as uint, ptr::null()); - do vec::as_imm_buf(elts) |buf, _len| { - llvm::LLVMGetStructElementTypes(ty, buf); - } + let mut elts = vec::from_elem(n as uint, ptr::null()); + llvm::LLVMGetStructElementTypes(ty, + ptr::to_mut_unsafe_ptr(&mut elts[0])); return elts; } diff --git a/src/librustc/middle/trans/reachable.rs b/src/librustc/middle/trans/reachable.rs index 7b03460bb7fc5..a3df1c630f5a6 100644 --- a/src/librustc/middle/trans/reachable.rs +++ b/src/librustc/middle/trans/reachable.rs @@ -170,6 +170,18 @@ fn traverse_inline_body(cx: ctx, body: blk) { _ => () } } + expr_method_call(*) => { + match cx.method_map.find(e.id) { + Some({origin: typeck::method_static(did), _}) => { + traverse_def_id(cx, did); + } + Some(_) => {} + None => { + cx.tcx.sess.span_bug(e.span, ~"expr_method_call not in \ + method map"); + } + } + } _ => () } visit::visit_expr(e, cx, v); diff --git a/src/librustc/middle/trans/type_use.rs b/src/librustc/middle/trans/type_use.rs index 14744c4cc9eb1..93c79e24ce82a 100644 --- a/src/librustc/middle/trans/type_use.rs +++ b/src/librustc/middle/trans/type_use.rs @@ -173,6 +173,25 @@ fn node_type_needs(cx: ctx, use_: uint, id: node_id) { type_needs(cx, use_, ty::node_id_to_type(cx.ccx.tcx, id)); } +fn mark_for_method_call(cx: ctx, e_id: node_id) { + do option::iter(&cx.ccx.maps.method_map.find(e_id)) |mth| { + match mth.origin { + typeck::method_static(did) => { + do cx.ccx.tcx.node_type_substs.find(e_id).iter |ts| { + let type_uses = type_uses_for(cx.ccx, did, ts.len()); + for vec::each2(type_uses, *ts) |uses, subst| { + type_needs(cx, *uses, *subst) + } + } + } + typeck::method_param({param_num: param, _}) => { + cx.uses[param] |= use_tydesc; + } + typeck::method_trait(*) | typeck::method_self(*) => (), + } + } +} + fn mark_for_expr(cx: ctx, e: @expr) { match e.node { expr_vstore(_, _) | @@ -231,23 +250,7 @@ fn mark_for_expr(cx: ctx, e: @expr) { // the chosen field. let base_ty = ty::node_id_to_type(cx.ccx.tcx, base.id); type_needs(cx, use_repr, ty::type_autoderef(cx.ccx.tcx, base_ty)); - - do option::iter(&cx.ccx.maps.method_map.find(e.id)) |mth| { - match mth.origin { - typeck::method_static(did) => { - do cx.ccx.tcx.node_type_substs.find(e.id).iter |ts| { - let type_uses = type_uses_for(cx.ccx, did, ts.len()); - for vec::each2(type_uses, *ts) |uses, subst| { - type_needs(cx, *uses, *subst) - } - } - } - typeck::method_param({param_num: param, _}) => { - cx.uses[param] |= use_tydesc; - } - typeck::method_trait(*) | typeck::method_self(*) => (), - } - } + mark_for_method_call(cx, e.id); } expr_log(_, _, val) => { node_type_needs(cx, use_tydesc, val.id); @@ -264,6 +267,21 @@ fn mark_for_expr(cx: ctx, e: @expr) { } } } + expr_method_call(rcvr, _, _, _, _) => { + let base_ty = ty::node_id_to_type(cx.ccx.tcx, rcvr.id); + type_needs(cx, use_repr, ty::type_autoderef(cx.ccx.tcx, base_ty)); + + for ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx, + e.callee_id)).each |a| { + match a.mode { + expl(by_move) | expl(by_copy) | expl(by_val) => { + type_needs(cx, use_repr, a.ty); + } + _ => () + } + } + mark_for_method_call(cx, e.id); + } expr_paren(e) => mark_for_expr(cx, e), expr_match(*) | expr_block(_) | expr_if(*) | expr_while(*) | expr_fail(_) | expr_break(_) | expr_again(_) | diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index ad6ca83fe4ea0..6d8bfa20a5002 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3352,6 +3352,7 @@ fn expr_kind(tcx: ctxt, } ast::expr_call(*) | + ast::expr_method_call(*) | ast::expr_rec(*) | ast::expr_struct(*) | ast::expr_tup(*) | diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 32d7181b40d41..0d425303d97bf 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -1100,24 +1100,22 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, return bot; } - // A generic function for doing all of the checking for call expressions - fn check_call(fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id, - f: @ast::expr, args: ~[@ast::expr]) -> bool { - - // Index expressions need to be handled seperately, to inform - // them that they appear in call position. - let mut bot = match f.node { - ast::expr_field(base, field, tys) => { - check_field(fcx, f, true, base, field, tys) - } - _ => check_expr(fcx, f, None) - }; - let fn_ty = fcx.expr_ty(f); + // A generic function for doing all of the checking for call or + // method expressions + fn check_call_or_method(fcx: @fn_ctxt, + sp: span, + call_expr_id: ast::node_id, + fn_ty: ty::t, + expr: @ast::expr, + args: ~[@ast::expr], + bot: bool) + -> bool { + let mut bot = bot; // Call the generic checker. let fty = { let r = check_call_inner(fcx, sp, call_expr_id, - fn_ty, f, args, DontDerefArgs); + fn_ty, expr, args, DontDerefArgs); bot |= r.bot; r.fty }; @@ -1138,6 +1136,79 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, } } + // A generic function for doing all of the checking for call expressions + fn check_call(fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id, + f: @ast::expr, args: ~[@ast::expr]) -> bool { + + // Index expressions need to be handled separately, to inform them + // that they appear in call position. + let mut bot = match f.node { + ast::expr_field(base, field, tys) => { + check_field(fcx, f, true, base, field, tys) + } + _ => check_expr(fcx, f, None) + }; + + check_call_or_method(fcx, + sp, + call_expr_id, + fcx.expr_ty(f), + f, + args, + bot) + } + + // Checks a method call. + fn check_method_call(fcx: @fn_ctxt, + expr: @ast::expr, + rcvr: @ast::expr, + method_name: ast::ident, + args: ~[@ast::expr], + tps: ~[@ast::Ty]) + -> bool { + let bot = check_expr(fcx, rcvr, None); + let expr_t = structurally_resolved_type(fcx, + expr.span, + fcx.expr_ty(rcvr)); + + let tps = tps.map(|ast_ty| fcx.to_ty(*ast_ty)); + match method::lookup(fcx, + expr, + rcvr, + expr.callee_id, + method_name, + expr_t, + tps, + DontDerefArgs) { + Some(entry) => { + fcx.ccx.method_map.insert(expr.id, entry); + } + None => { + fcx.type_error_message(expr.span, + |actual| { + fmt!("type `%s` does not implement any method in scope \ + named `%s`", + actual, + fcx.ccx.tcx.sess.str_of(method_name)) + }, + expr_t, + None); + + // Add error type for the result + fcx.write_ty(expr.id, ty::mk_err(fcx.ccx.tcx)); + fcx.write_ty(expr.callee_id, ty::mk_err(fcx.ccx.tcx)); + } + } + + check_call_or_method(fcx, + expr.span, + expr.id, + fcx.node_ty(expr.callee_id), + expr, + args, + bot) + } + // A generic function for checking for or for-each loops fn check_for(fcx: @fn_ctxt, local: @ast::local, element_ty: ty::t, body: ast::blk, @@ -2087,6 +2158,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, ast::expr_call(f, args, _) => { bot = check_call(fcx, expr.span, expr.id, f, args); } + ast::expr_method_call(rcvr, ident, tps, args, _) => { + bot = check_method_call(fcx, expr, rcvr, ident, args, tps); + } ast::expr_cast(e, t) => { bot = check_expr(fcx, e, None); let t_1 = fcx.to_ty(t); diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index b70c85474d596..f45bef2eb5ffc 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -199,6 +199,17 @@ fn visit_expr(expr: @ast::expr, &&rcx: @rcx, v: rvt) { } } + ast::expr_method_call(rcvr, _, _, args, _) => { + // Check for a.b() where b is a method. Ensure that + // any types in the callee are valid for the entire + // method call. + + constrain_auto_ref(rcx, rcvr); + for args.each |arg| { + constrain_auto_ref(rcx, *arg); + } + } + ast::expr_cast(source, _) => { // Determine if we are casting `source` to an trait // instance. If so, we have to be sure that the type of diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 7de2de708cc98..3ee0d74fb68ae 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -557,7 +557,7 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) { // Must resolve bounds on methods with bounded params ast::expr_field(*) | ast::expr_binary(*) | ast::expr_unary(*) | ast::expr_assign_op(*) | - ast::expr_index(*) => { + ast::expr_index(*) | ast::expr_method_call(*) => { match ty::method_call_bounds(cx.tcx, cx.method_map, ex.id) { Some(bounds) => { if has_trait_bounds(*bounds) { diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index 3ac94297191b4..ba2c9416f569f 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -158,6 +158,11 @@ fn visit_expr(e: @ast::expr, wbcx: wb_ctxt, v: wb_vt) { maybe_resolve_type_vars_for_node(wbcx, e.span, e.callee_id); } + ast::expr_method_call(*) => { + // We must always have written in a callee ID type for these. + resolve_type_vars_for_node(wbcx, e.span, e.callee_id); + } + _ => () } visit::visit_expr(e, wbcx, v); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 252ccdc3e1bbf..1eb734a69d357 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -714,7 +714,7 @@ impl blk_check_mode : cmp::Eq { #[auto_serialize] #[auto_deserialize] type expr = {id: node_id, callee_id: node_id, node: expr_, span: span}; -// Extra node ID is only used for index, assign_op, unary, binary +// Extra node ID is only used for index, assign_op, unary, binary, method call #[auto_serialize] #[auto_deserialize] @@ -728,6 +728,7 @@ enum expr_ { expr_vec(~[@expr], mutability), expr_rec(~[field], Option<@expr>), expr_call(@expr, ~[@expr], bool), // True iff last argument is a block + expr_method_call(@expr, ident, ~[@Ty], ~[@expr], bool), // Ditto expr_tup(~[@expr]), expr_binary(binop, @expr, @expr), expr_unary(unop, @expr), diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 9a1f3e7f04e80..03a9729477aba 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -409,6 +409,13 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ { fld.map_exprs(|x| fld.fold_expr(x), args), blk) } + expr_method_call(f, i, tps, args, blk) => { + expr_method_call(fld.fold_expr(f), + fld.fold_ident(i), + vec::map(tps, |x| fld.fold_ty(*x)), + fld.map_exprs(|x| fld.fold_expr(x), args), + blk) + } expr_binary(binop, lhs, rhs) => { expr_binary(binop, fld.fold_expr(lhs), fld.fold_expr(rhs)) } diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs index 0d370525d1789..e0210feb9de6c 100644 --- a/src/libsyntax/parse/classify.rs +++ b/src/libsyntax/parse/classify.rs @@ -6,9 +6,13 @@ use ast_util::operator_prec; fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool { match e.node { - ast::expr_if(*) | ast::expr_match(*) | ast::expr_block(_) - | ast::expr_while(*) | ast::expr_loop(*) - | ast::expr_call(_, _, true) => false, + ast::expr_if(*) + | ast::expr_match(*) + | ast::expr_block(_) + | ast::expr_while(*) + | ast::expr_loop(*) + | ast::expr_call(_, _, true) + | ast::expr_method_call(_, _, _, _, true) => false, _ => true } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 96364790956b7..6f1127518fce5 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -34,10 +34,10 @@ use ast::{_mod, add, arg, arm, attribute, expr_call, expr_cast, expr_copy, expr_do_body, expr_fail, expr_field, expr_fn, expr_fn_block, expr_if, expr_index, expr_lit, expr_log, expr_loop, expr_loop_body, expr_mac, - expr_paren, expr_path, expr_rec, expr_repeat, expr_ret, - expr_swap, expr_struct, expr_tup, expr_unary, expr_unary_move, - expr_vec, expr_vstore, expr_vstore_mut_box, expr_while, - extern_fn, field, fn_decl, + expr_method_call, expr_paren, expr_path, expr_rec, expr_repeat, + expr_ret, expr_swap, expr_struct, expr_tup, expr_unary, + expr_unary_move, expr_vec, expr_vstore, expr_vstore_mut_box, + expr_while, extern_fn, field, fn_decl, foreign_item, foreign_item_const, foreign_item_fn, foreign_mod, ident, impure_fn, infer, inherited, item, item_, item_class, item_const, item_enum, item_fn, @@ -1212,8 +1212,26 @@ impl Parser { self.expect(token::LT); self.parse_seq_to_gt(Some(token::COMMA), |p| p.parse_ty(false)) - } else { ~[] }; - e = self.mk_expr(lo, hi, expr_field(e, i, tys)); + } else { + ~[] + }; + + // expr.f() method call + match copy self.token { + token::LPAREN if self.permits_call() => { + let es = self.parse_unspanned_seq( + token::LPAREN, token::RPAREN, + seq_sep_trailing_disallowed(token::COMMA), + |p| p.parse_expr()); + hi = self.span.hi; + + let nd = expr_method_call(e, i, tys, es, false); + e = self.mk_expr(lo, hi, move nd); + } + _ => { + e = self.mk_expr(lo, hi, expr_field(e, i, tys)); + } + } } _ => self.unexpected() } @@ -1674,7 +1692,16 @@ impl Parser { @{node: expr_call(f, args, true), .. *e} } - expr_path(*) | expr_field(*) | expr_call(*) | expr_paren(*) => { + expr_method_call(f, i, tps, args, false) => { + let block = self.parse_lambda_block_expr(); + let last_arg = self.mk_expr(block.span.lo, block.span.hi, + ctor(block)); + let args = vec::append(args, ~[last_arg]); + @{node: expr_method_call(f, i, tps, args, true), + .. *e} + } + expr_path(*) | expr_field(*) | expr_call(*) | expr_method_call(*) | + expr_paren(*) => { let block = self.parse_lambda_block_expr(); let last_arg = self.mk_expr(block.span.lo, block.span.hi, ctor(block)); diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 5ad3c051c5977..e13c6da403ff7 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1041,6 +1041,50 @@ fn print_expr_vstore(s: ps, t: ast::expr_vstore) { } } +fn print_call_pre(s: ps, + has_block: bool, + base_args: &mut ~[@ast::expr]) + -> Option<@ast::expr> { + if has_block { + let blk_arg = base_args.pop(); + match blk_arg.node { + ast::expr_loop_body(_) => { head(s, ~"for"); } + ast::expr_do_body(_) => { head(s, ~"do"); } + _ => {} + } + Some(blk_arg) + } else { + None + } +} + +fn print_call_post(s: ps, + has_block: bool, + blk: &Option<@ast::expr>, + base_args: &mut ~[@ast::expr]) { + if !has_block || base_args.is_not_empty() { + popen(s); + commasep_exprs(s, inconsistent, *base_args); + pclose(s); + } + if has_block { + nbsp(s); + match blk.get().node { + // need to handle closures specifically + ast::expr_do_body(e) | ast::expr_loop_body(e) => { + end(s); // we close our head box; closure + // will create it's own. + print_expr(s, e); + end(s); // close outer box, as closures don't + } + _ => { + // not sure if this can happen. + print_expr(s, blk.get()); + } + } + } +} + fn print_expr(s: ps, &&expr: @ast::expr) { fn print_field(s: ps, field: ast::field) { ibox(s, indent_unit); @@ -1135,38 +1179,23 @@ fn print_expr(s: ps, &&expr: @ast::expr) { pclose(s); } ast::expr_call(func, args, has_block) => { - let mut base_args = args; - let blk = if has_block { - let blk_arg = base_args.pop(); - match blk_arg.node { - ast::expr_loop_body(_) => { head(s, ~"for"); } - ast::expr_do_body(_) => { head(s, ~"do"); } - _ => {} - } - Some(blk_arg) - } else { None }; + let mut base_args = copy args; + let blk = print_call_pre(s, has_block, &mut base_args); print_expr(s, func); - if !has_block || base_args.is_not_empty() { - popen(s); - commasep_exprs(s, inconsistent, base_args); - pclose(s); - } - if has_block { - nbsp(s); - match blk.get().node { - // need to handle closures specifically - ast::expr_do_body(e) | ast::expr_loop_body(e) => { - end(s); // we close our head box; closure - // will create it's own. - print_expr(s, e); - end(s); // close outer box, as closures don't - } - _ => { - // not sure if this can happen. - print_expr(s, blk.get()); - } - } + print_call_post(s, has_block, &blk, &mut base_args); + } + ast::expr_method_call(func, ident, tys, args, has_block) => { + let mut base_args = copy args; + let blk = print_call_pre(s, has_block, &mut base_args); + print_expr(s, func); + word(s.s, ~"."); + print_ident(s, ident); + if vec::len(tys) > 0u { + word(s.s, ~"::<"); + commasep(s, inconsistent, tys, print_type); + word(s.s, ~">"); } + print_call_post(s, has_block, &blk, &mut base_args); } ast::expr_binary(op, lhs, rhs) => { print_expr(s, lhs); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index fbb1bc9117211..235bd942d30d9 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -408,6 +408,11 @@ fn visit_expr(ex: @expr, e: E, v: vt) { visit_exprs(args, e, v); (v.visit_expr)(callee, e, v); } + expr_method_call(callee, _, tys, args, _) => { + (v.visit_expr)(callee, e, v); + for tys.each |tp| { (v.visit_ty)(*tp, e, v); } + visit_exprs(args, e, v); + } expr_binary(_, a, b) => { (v.visit_expr)(a, e, v); (v.visit_expr)(b, e, v); } diff --git a/src/test/run-pass/const-nullary-enum.rs b/src/test/run-pass/const-nullary-enum.rs new file mode 100644 index 0000000000000..644517deca88a --- /dev/null +++ b/src/test/run-pass/const-nullary-enum.rs @@ -0,0 +1,15 @@ +enum Foo { + Bar, + Baz, + Boo, +} + +const X: Foo = Bar; + +fn main() { + match X { + Bar => {} + Baz | Boo => fail + } +} + diff --git a/src/test/run-pass/const-tuple-struct.rs b/src/test/run-pass/const-tuple-struct.rs new file mode 100644 index 0000000000000..d10d490740be2 --- /dev/null +++ b/src/test/run-pass/const-tuple-struct.rs @@ -0,0 +1,13 @@ +struct Bar(int, int); + +const X: Bar = Bar(1, 2); + +fn main() { + match X { + Bar(x, y) => { + assert x == 1; + assert y == 2; + } + } +} + diff --git a/src/test/run-pass/const-unit-struct.rs b/src/test/run-pass/const-unit-struct.rs new file mode 100644 index 0000000000000..c0a0201ed74ba --- /dev/null +++ b/src/test/run-pass/const-unit-struct.rs @@ -0,0 +1,10 @@ +struct Foo; + +const X: Foo = Foo; + +fn main() { + match X { + Foo => {} + } +} +