-
-
Save Manishearth/045ee457d6f81183ec6b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct MyVisitor { | |
in_allowed: bool, | |
in_dropper: bool, | |
map: HashMap<NodeId, NodeId>, | |
current_block: NodeId, | |
} | |
impl Visitor for MyVisitor { | |
visit_stmt() { | |
// If it's a StmtDecl | |
// Check the type of the thing it's assigning to (expr_ty) | |
// If that type is a Chan, add it's nodeid and the current block | |
// nodeid to the map | |
// You can call visit::walk_stmt(self, stmt) after this, but you don't need to and it might require | |
// special casing since the ident will be visited inside of it and you have to ignore that one. | |
} | |
visit_ident(...) { | |
if !self.in_allowed { | |
// Do a defmap lookup | |
// If it's a DefLocal, check if the corresponding NodeId is in the map | |
// if so, error. The variable is being used when it shouldn't be! | |
} | |
if self.in_dropper { | |
// remove the corresponding def from the hashmap | |
} | |
} | |
// I'm assuming that all allowed methods consuming chan | |
// don't use self (so are called by `do_something(chan)` and | |
// not `chan.do_something()`. If not, we can add another case here | |
// for exprmethodcall. All allowed methods should be marked as `#[allow_chan]` | |
// or whatever | |
visit_expr(self, expr) { | |
if self.in_allowed { | |
visit::walk_expr(self, expr); | |
return | |
} | |
match expr { | |
ExprCall(ex, ....) => { | |
// do a defmap lookup on ex.id | |
// use middle::ty::has_attr() to see if it | |
// has the "allow_chan" attribute | |
// Also check for the `chan_dropper` | |
// attribute for functions that are allowed to drop it | |
if has_drop_attribute { | |
self.in_dropper = true; | |
self.in_allowed = true; | |
visit::walk_expr(self, expr); | |
self.in_dropper = false; | |
self.in_allowed = false; | |
return | |
} | |
if has_allow_attribute { | |
self.in_allowed = true; | |
visit::walk_expr(self, expr); | |
self.in_allowed = false; | |
return | |
} | |
} | |
} | |
visit::walk_expr(self, expr); | |
} | |
visit_block(self, block) { | |
let id = block.id | |
self.current_block = id; // will change whilst recursing | |
visit::walk_block(self, block) | |
// Check if there still is an entry linked to `id` | |
// in self.map. If so, error, the variable was implicitly dropped | |
} | |
} | |
// Example of an allowed function | |
#[allow_chan] | |
fn send<T>(c: Chan<T>, Data<T>) -> Chan<T> { | |
// do stuff | |
c | |
} | |
// Example of function allowed to drop it | |
#[chan_dropper] | |
fn drop_chan<T>(c: Chan<T>) { // DO NOT return the chan! | |
// do stuff | |
} | |
// Now, to bundle all this up, create a LintPass, and within `check_fn`, have your visitor walk down the function | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Drop protected paper