Skip to content

Commit bf06dea

Browse files
committed
Disallow implicitly binding methods in typechecker. Closes #2189.
1 parent e4362a5 commit bf06dea

File tree

3 files changed

+108
-80
lines changed

3 files changed

+108
-80
lines changed

src/rustc/middle/typeck/check.rs

Lines changed: 104 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,14 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
814814
fn check_call(fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id,
815815
f: @ast::expr, args: ~[@ast::expr]) -> bool {
816816

817-
let mut bot = check_expr(fcx, f, none);
817+
// Index expressions need to be handled seperately, to inform
818+
// them that they appear in call position.
819+
let mut bot = alt f.node {
820+
ast::expr_field(base, field, tys) {
821+
check_field(fcx, f, true, base, field, tys)
822+
}
823+
_ { check_expr(fcx, f, none) }
824+
};
818825
let fn_ty = fcx.expr_ty(f);
819826

820827
// Call the generic checker.
@@ -1051,6 +1058,101 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
10511058
is_loop_body, some(fcx));
10521059
}
10531060

1061+
// Check field access expressions
1062+
fn check_field(fcx: @fn_ctxt, expr: @ast::expr, is_callee: bool,
1063+
base: @ast::expr, field: ast::ident, tys: ~[@ast::ty])
1064+
-> bool {
1065+
let tcx = fcx.ccx.tcx;
1066+
let bot = check_expr(fcx, base, none);
1067+
let expr_t = structurally_resolved_type(fcx, expr.span,
1068+
fcx.expr_ty(base));
1069+
let base_t = do_autoderef(fcx, expr.span, expr_t);
1070+
let mut handled = false;
1071+
let n_tys = vec::len(tys);
1072+
alt structure_of(fcx, expr.span, base_t) {
1073+
ty::ty_rec(fields) {
1074+
alt ty::field_idx(field, fields) {
1075+
some(ix) {
1076+
if n_tys > 0u {
1077+
tcx.sess.span_err(expr.span,
1078+
"can't provide type parameters \
1079+
to a field access");
1080+
}
1081+
fcx.write_ty(expr.id, fields[ix].mt.ty);
1082+
handled = true;
1083+
}
1084+
_ {}
1085+
}
1086+
}
1087+
ty::ty_class(base_id, substs) {
1088+
// This is just for fields -- the same code handles
1089+
// methods in both classes and traits
1090+
1091+
// (1) verify that the class id actually has a field called
1092+
// field
1093+
#debug("class named %s", ty_to_str(tcx, base_t));
1094+
/*
1095+
check whether this is a self-reference or not, which
1096+
determines whether we look at all fields or only public
1097+
ones
1098+
*/
1099+
let cls_items = if self_ref(fcx, base.id) {
1100+
// base expr is "self" -- consider all fields
1101+
ty::lookup_class_fields(tcx, base_id)
1102+
}
1103+
else {
1104+
lookup_public_fields(tcx, base_id)
1105+
};
1106+
alt lookup_field_ty(tcx, base_id, cls_items, field, substs) {
1107+
some(field_ty) {
1108+
// (2) look up what field's type is, and return it
1109+
fcx.write_ty(expr.id, field_ty);
1110+
handled = true;
1111+
}
1112+
none {}
1113+
}
1114+
}
1115+
_ {}
1116+
}
1117+
if !handled {
1118+
let tps = vec::map(tys, |ty| fcx.to_ty(ty));
1119+
let is_self_ref = self_ref(fcx, base.id);
1120+
1121+
// this will be the call or block that immediately
1122+
// encloses the method call
1123+
let borrow_scope = fcx.tcx().region_map.get(expr.id);
1124+
1125+
let lkup = method::lookup(fcx, expr, base, borrow_scope,
1126+
expr.id, field, expr_t, tps,
1127+
is_self_ref);
1128+
alt lkup.method() {
1129+
some(entry) {
1130+
fcx.ccx.method_map.insert(expr.id, entry);
1131+
1132+
// If we have resolved to a method but this is not in
1133+
// a callee position, error
1134+
if !is_callee {
1135+
tcx.sess.span_err(
1136+
expr.span,
1137+
"attempted to take value of method \
1138+
(try writing an anonymous function)");
1139+
}
1140+
}
1141+
none {
1142+
let t_err = fcx.infcx.resolve_type_vars_if_possible(expr_t);
1143+
let msg = #fmt["attempted access of field `%s` on type `%s`, \
1144+
but no public field or method with that name \
1145+
was found",
1146+
*field, fcx.infcx.ty_to_str(t_err)];
1147+
tcx.sess.span_err(expr.span, msg);
1148+
// NB: Adding a bogus type to allow typechecking to continue
1149+
fcx.write_ty(expr.id, fcx.infcx.next_ty_var());
1150+
}
1151+
}
1152+
}
1153+
ret bot;
1154+
}
1155+
10541156

10551157
let tcx = fcx.ccx.tcx;
10561158
let id = expr.id;
@@ -1489,84 +1591,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
14891591
}
14901592
}
14911593
ast::expr_field(base, field, tys) {
1492-
bot |= check_expr(fcx, base, none);
1493-
let expr_t = structurally_resolved_type(fcx, expr.span,
1494-
fcx.expr_ty(base));
1495-
let base_t = do_autoderef(fcx, expr.span, expr_t);
1496-
let mut handled = false;
1497-
let n_tys = vec::len(tys);
1498-
alt structure_of(fcx, expr.span, base_t) {
1499-
ty::ty_rec(fields) {
1500-
alt ty::field_idx(field, fields) {
1501-
some(ix) {
1502-
if n_tys > 0u {
1503-
tcx.sess.span_err(expr.span,
1504-
"can't provide type parameters \
1505-
to a field access");
1506-
}
1507-
fcx.write_ty(id, fields[ix].mt.ty);
1508-
handled = true;
1509-
}
1510-
_ {}
1511-
}
1512-
}
1513-
ty::ty_class(base_id, substs) {
1514-
// This is just for fields -- the same code handles
1515-
// methods in both classes and traits
1516-
1517-
// (1) verify that the class id actually has a field called
1518-
// field
1519-
#debug("class named %s", ty_to_str(tcx, base_t));
1520-
/*
1521-
check whether this is a self-reference or not, which
1522-
determines whether we look at all fields or only public
1523-
ones
1524-
*/
1525-
let cls_items = if self_ref(fcx, base.id) {
1526-
// base expr is "self" -- consider all fields
1527-
ty::lookup_class_fields(tcx, base_id)
1528-
}
1529-
else {
1530-
lookup_public_fields(tcx, base_id)
1531-
};
1532-
alt lookup_field_ty(tcx, base_id, cls_items, field, substs) {
1533-
some(field_ty) {
1534-
// (2) look up what field's type is, and return it
1535-
fcx.write_ty(id, field_ty);
1536-
handled = true;
1537-
}
1538-
none {}
1539-
}
1540-
}
1541-
_ {}
1542-
}
1543-
if !handled {
1544-
let tps = vec::map(tys, |ty| fcx.to_ty(ty));
1545-
let is_self_ref = self_ref(fcx, base.id);
1546-
1547-
// this will be the call or block that immediately
1548-
// encloses the method call
1549-
let borrow_scope = fcx.tcx().region_map.get(expr.id);
1550-
1551-
let lkup = method::lookup(fcx, expr, base, borrow_scope,
1552-
expr.id, field, expr_t, tps,
1553-
is_self_ref);
1554-
alt lkup.method() {
1555-
some(entry) {
1556-
fcx.ccx.method_map.insert(id, entry);
1557-
}
1558-
none {
1559-
let t_err = fcx.infcx.resolve_type_vars_if_possible(expr_t);
1560-
let msg = #fmt["attempted access of field `%s` on type `%s`, \
1561-
but no public field or method with that name \
1562-
was found",
1563-
*field, fcx.infcx.ty_to_str(t_err)];
1564-
tcx.sess.span_err(expr.span, msg);
1565-
// NB: Adding a bogus type to allow typechecking to continue
1566-
fcx.write_ty(id, fcx.infcx.next_ty_var());
1567-
}
1568-
}
1569-
}
1594+
bot = check_field(fcx, expr, false, base, field, tys);
15701595
}
15711596
ast::expr_index(base, idx) {
15721597
bot |= check_expr(fcx, base, none);

src/test/compile-fail/assign-to-method.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ class cat {
1111

1212
fn main() {
1313
let nyan : cat = cat(52u, 99);
14-
nyan.speak = fn@() { #debug["meow"]; }; //~ ERROR assigning to method
14+
nyan.speak = fn@() { #debug["meow"]; }; //~ ERROR attempted to take value of method
1515
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
let _f = 10.times; //~ ERROR attempted to take value of method
3+
}

0 commit comments

Comments
 (0)