Skip to content

Commit 0699acb

Browse files
committed
Rudimentary checking of safe alias returns
1 parent 63519d9 commit 0699acb

File tree

1 file changed

+52
-27
lines changed

1 file changed

+52
-27
lines changed

src/comp/middle/alias.rs

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type binding = @{node_id: node_id,
2222
unsafe_tys: [ty::t],
2323
mutable ok: valid,
2424
mutable copied: copied};
25-
type scope = [binding]; // {bs: [binding], ret_style: ast::ret_style}
25+
type scope = {bs: [binding], ret_style: ast::ret_style};
2626

2727
fn mk_binding(cx: ctx, id: node_id, span: span, root_var: option::t<node_id>,
2828
unsafe: [ty::t]) -> binding {
@@ -47,25 +47,32 @@ fn check_crate(tcx: ty::ctxt, crate: @ast::crate) -> copy_map {
4747
local_map: std::map::new_int_hash(),
4848
mutable next_local: 0u,
4949
copy_map: std::map::new_int_hash()};
50-
let v = @{visit_fn: visit_fn,
50+
let v = @{visit_fn: bind visit_fn(cx, _, _, _, _, _, _, _),
5151
visit_expr: bind visit_expr(cx, _, _, _),
5252
visit_decl: bind visit_decl(cx, _, _, _)
5353
with *visit::default_visitor::<scope>()};
54-
visit::visit_crate(*crate, [], visit::mk_vt(v));
54+
visit::visit_crate(*crate, {bs: [], ret_style: ast::return_val},
55+
visit::mk_vt(v));
5556
tcx.sess.abort_if_errors();
5657
ret cx.copy_map;
5758
}
5859

59-
fn visit_fn(f: ast::_fn, _tp: [ast::ty_param], _sp: span, _name: fn_ident,
60-
_id: ast::node_id, sc: scope, v: vt<scope>) {
60+
fn visit_fn(cx: @ctx, f: ast::_fn, _tp: [ast::ty_param], _sp: span,
61+
_name: fn_ident, _id: ast::node_id, sc: scope, v: vt<scope>) {
6162
visit::visit_fn_decl(f.decl, sc, v);
62-
let scope = alt f.proto {
63+
let bs = alt f.proto {
6364
// Blocks need to obey any restrictions from the enclosing scope.
64-
ast::proto_block. | ast::proto_closure. { sc }
65+
ast::proto_block. | ast::proto_closure. { sc.bs }
6566
// Non capturing functions start out fresh.
6667
_ { [] }
6768
};
68-
v.visit_block(f.body, scope, v);
69+
if f.decl.cf == ast::return_ref && !is_none(f.body.node.expr) {
70+
// FIXME this will be easier to lift once have DPS
71+
cx.tcx.sess.span_err(option::get(f.body.node.expr).span,
72+
"reference-returning functions may not " +
73+
"return implicitly");
74+
}
75+
v.visit_block(f.body, {bs: bs, ret_style: f.decl.cf}, v);
6976
}
7077

7178
fn visit_expr(cx: @ctx, ex: @ast::expr, sc: scope, v: vt<scope>) {
@@ -111,7 +118,9 @@ fn visit_expr(cx: @ctx, ex: @ast::expr, sc: scope, v: vt<scope>) {
111118
check_assign(cx, dest, src, sc, v);
112119
}
113120
ast::expr_ret(oexpr) {
114-
121+
if sc.ret_style == ast::return_ref && !is_none(oexpr) {
122+
check_ret_ref(*cx, option::get(oexpr));
123+
}
115124
handled = false;
116125
}
117126
_ { handled = false; }
@@ -256,12 +265,33 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr]) -> [binding] {
256265
ret bindings;
257266
}
258267

268+
fn check_ret_ref(cx: ctx, expr: @ast::expr) {
269+
let root = expr_root(cx.tcx, expr, false);
270+
let bad = none;
271+
alt path_def(cx, root.ex) {
272+
none. { bad = some("temporary"); }
273+
some(ast::def_arg(_, mode)) {
274+
if mode == ast::by_move { bad = some("move-mode parameter"); }
275+
if mut_field(root.ds) { bad = some("mutable field"); }
276+
}
277+
// FIXME allow references to constants and static items?
278+
_ { bad = some("non-argument value"); }
279+
}
280+
alt bad {
281+
some(name) {
282+
cx.tcx.sess.span_err(expr.span, "can not return a reference " +
283+
"to a " + name);
284+
}
285+
_ {}
286+
}
287+
}
288+
259289
fn check_alt(cx: ctx, input: @ast::expr, arms: [ast::arm], sc: scope,
260290
v: vt<scope>) {
261291
v.visit_expr(input, sc, v);
262292
let root = expr_root(cx.tcx, input, true);
263293
for a: ast::arm in arms {
264-
let new_sc = sc;
294+
let new_bs = sc.bs;
265295
let root_var = path_def_id(cx, root.ex);
266296
let pat_id_map = ast_util::pat_id_map(a.pats[0]);
267297
type info = {id: node_id, mutable unsafe: [ty::t], span: span};
@@ -283,11 +313,11 @@ fn check_alt(cx: ctx, input: @ast::expr, arms: [ast::arm], sc: scope,
283313
}
284314
}
285315
for info in binding_info {
286-
new_sc += [mk_binding(cx, info.id, info.span, root_var,
287-
info.unsafe)];
316+
new_bs += [mk_binding(cx, info.id, info.span, root_var,
317+
copy info.unsafe)];
288318
}
289319
register_locals(cx, a.pats[0]);
290-
visit::visit_arm(a, new_sc, v);
320+
visit::visit_arm(a, {bs: new_bs with sc}, v);
291321
}
292322
}
293323

@@ -296,13 +326,13 @@ fn check_for_each(cx: ctx, local: @ast::local, call: @ast::expr,
296326
v.visit_expr(call, sc, v);
297327
alt call.node {
298328
ast::expr_call(f, args) {
299-
let new_sc = sc + check_call(cx, f, args);
329+
let new_bs = sc.bs + check_call(cx, f, args);
300330
for proot in *pattern_roots(cx.tcx, [], local.node.pat) {
301-
new_sc += [mk_binding(cx, proot.id, proot.span, none,
331+
new_bs += [mk_binding(cx, proot.id, proot.span, none,
302332
inner_mut(proot.ds))];
303333
}
304334
register_locals(cx, local.node.pat);
305-
visit::visit_block(blk, new_sc, v);
335+
visit::visit_block(blk, {bs: new_bs with sc}, v);
306336
}
307337
}
308338
}
@@ -324,13 +354,13 @@ fn check_for(cx: ctx, local: @ast::local, seq: @ast::expr, blk: ast::blk,
324354
_ {}
325355
}
326356
let root_var = path_def_id(cx, root.ex);
327-
let new_sc = sc;
357+
let new_bs = sc.bs;
328358
for proot in *pattern_roots(cx.tcx, ext_ds, local.node.pat) {
329-
new_sc += [mk_binding(cx, proot.id, proot.span, root_var,
359+
new_bs += [mk_binding(cx, proot.id, proot.span, root_var,
330360
inner_mut(proot.ds))];
331361
}
332362
register_locals(cx, local.node.pat);
333-
visit::visit_block(blk, new_sc, v);
363+
visit::visit_block(blk, {bs: new_bs with sc}, v);
334364
}
335365

336366
fn check_var(cx: ctx, ex: @ast::expr, p: ast::path, id: ast::node_id,
@@ -341,7 +371,7 @@ fn check_var(cx: ctx, ex: @ast::expr, p: ast::path, id: ast::node_id,
341371
let my_local_id =
342372
alt cx.local_map.find(my_defnum) { some(local(id)) { id } _ { 0u } };
343373
let var_t = ty::expr_ty(cx.tcx, ex);
344-
for b in sc {
374+
for b in sc.bs {
345375
// excludes variables introduced since the alias was made
346376
if my_local_id < b.local_id {
347377
for ty in b.unsafe_tys {
@@ -360,7 +390,7 @@ fn check_lval(cx: @ctx, dest: @ast::expr, sc: scope, v: vt<scope>) {
360390
ast::expr_path(p) {
361391
let def = cx.tcx.def_map.get(dest.id);
362392
let dnum = ast_util::def_id_of_def(def).node;
363-
for b in sc {
393+
for b in sc.bs {
364394
if b.root_var == some(dnum) { b.ok = overwritten(dest.span, p); }
365395
}
366396
}
@@ -378,7 +408,7 @@ fn test_scope(cx: ctx, sc: scope, b: binding, p: ast::path) {
378408
let prob = b.ok;
379409
alt b.root_var {
380410
some(dn) {
381-
for other in sc {
411+
for other in sc.bs {
382412
if other.node_id == dn {
383413
prob = other.ok;
384414
if prob != valid { break; }
@@ -461,11 +491,6 @@ fn ty_can_unsafely_include(cx: ctx, needle: ty::t, haystack: ty::t, mut: bool)
461491
ret true;
462492
}
463493
ty::ty_obj(_) { ret true; }
464-
465-
466-
467-
468-
469494
// A type param may include everything, but can only be
470495
// treated as opaque downstream, and is thus safe unless we
471496
// saw mutable fields, in which case the whole thing can be

0 commit comments

Comments
 (0)