auto merge of #10132 : pcwalton/rust/proc, r=pcwalton

the feature gate for `once fn` if used with the `~` sigil.

r? @brson
This commit is contained in:
bors 2013-10-29 10:52:25 -07:00
commit fed48cc861
29 changed files with 250 additions and 70 deletions

View File

@ -48,7 +48,7 @@ pub fn run(lib_path: &str,
input: Option<~str>) -> Result {
let env = env + target_env(lib_path, prog);
let mut proc = run::Process::new(prog, args, run::ProcessOptions {
let mut process = run::Process::new(prog, args, run::ProcessOptions {
env: Some(env),
dir: None,
in_fd: None,
@ -57,9 +57,9 @@ pub fn run(lib_path: &str,
});
for input in input.iter() {
proc.input().write(input.as_bytes());
process.input().write(input.as_bytes());
}
let output = proc.finish_with_output();
let output = process.finish_with_output();
Result {
status: output.status,

View File

@ -132,7 +132,8 @@ impl Visitor<()> for Context {
fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
match t.node {
ast::ty_closure(closure) if closure.onceness == ast::Once => {
ast::ty_closure(closure) if closure.onceness == ast::Once &&
closure.sigil != ast::OwnedSigil => {
self.gate_feature("once_fns", t.span,
"once functions are \
experimental and likely to be removed");

View File

@ -656,7 +656,7 @@ impl<'self> CheckLoanCtxt<'self> {
fn check_move_out_from_expr(&self, expr: @ast::Expr) {
match expr.node {
ast::ExprFnBlock(*) => {
ast::ExprFnBlock(*) | ast::ExprProc(*) => {
// moves due to capture clauses are checked
// in `check_loans_in_fn`, so that we can
// give a better error message

View File

@ -307,7 +307,7 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt,
this.pop_repeating_id(body.id);
}
ast::ExprFnBlock(*) => {
ast::ExprFnBlock(*) | ast::ExprProc(*) => {
gather_moves::gather_captures(this.bccx, this.move_data, ex);
visit::walk_expr(this, ex, ());
}

View File

@ -409,6 +409,7 @@ impl CFGBuilder {
ast::ExprInlineAsm(*) |
ast::ExprSelf |
ast::ExprFnBlock(*) |
ast::ExprProc(*) |
ast::ExprLit(*) |
ast::ExprPath(*) => {
self.straightline(expr, pred, [])

View File

@ -49,7 +49,7 @@ impl Visitor<Context> for CheckLoopVisitor {
ExprLoop(ref b, _) => {
self.visit_block(b, Context { in_loop: true,.. cx });
}
ExprFnBlock(_, ref b) => {
ExprFnBlock(_, ref b) | ExprProc(_, ref b) => {
self.visit_block(b, Context { in_loop: false, can_ret: false });
}
ExprBreak(_) => {

View File

@ -431,7 +431,8 @@ impl<'self, O:DataFlowOperator> PropagationContext<'self, O> {
self.merge_with_entry_set(expr.id, in_out);
match expr.node {
ast::ExprFnBlock(ref decl, ref body) => {
ast::ExprFnBlock(ref decl, ref body) |
ast::ExprProc(ref decl, ref body) => {
if self.dfcx.oper.walk_closures() {
// In the absence of once fns, we must assume that
// every function body will execute more than

View File

@ -47,7 +47,7 @@ impl Visitor<int> for CollectFreevarsVisitor {
fn visit_expr(&mut self, expr:@ast::Expr, depth:int) {
match expr.node {
ast::ExprFnBlock(*) => {
ast::ExprFnBlock(*) | ast::ExprProc(*) => {
visit::walk_expr(self, expr, depth + 1)
}
ast::ExprPath(*) | ast::ExprSelf => {

View File

@ -485,7 +485,7 @@ fn visit_expr(v: &mut LivenessVisitor, expr: @Expr, this: @mut IrMaps) {
}
visit::walk_expr(v, expr, this);
}
ExprFnBlock(*) => {
ExprFnBlock(*) | ExprProc(*) => {
// Interesting control flow (for loops can contain labeled
// breaks or continues)
this.add_live_node_for_node(expr.id, ExprNode(expr.span));
@ -1023,8 +1023,8 @@ impl Liveness {
self.propagate_through_expr(e, succ)
}
ExprFnBlock(_, ref blk) => {
debug!("{} is an expr_fn_block",
ExprFnBlock(_, ref blk) | ExprProc(_, ref blk) => {
debug!("{} is an ExprFnBlock or ExprProc",
expr_to_str(expr, self.tcx.sess.intr()));
/*
@ -1498,7 +1498,8 @@ fn check_expr(this: &mut Liveness, expr: @Expr) {
ExprCast(*) | ExprUnary(*) | ExprRet(*) | ExprBreak(*) |
ExprAgain(*) | ExprLit(_) | ExprBlock(*) |
ExprMac(*) | ExprAddrOf(*) | ExprStruct(*) | ExprRepeat(*) |
ExprParen(*) | ExprFnBlock(*) | ExprPath(*) | ExprSelf(*) => {
ExprParen(*) | ExprFnBlock(*) | ExprProc(*) | ExprPath(*) |
ExprSelf(*) => {
visit::walk_expr(this, expr, ());
}
ExprForLoop(*) => fail!("non-desugared expr_for_loop")

View File

@ -424,7 +424,7 @@ impl mem_categorization_ctxt {
ast::ExprAddrOf(*) | ast::ExprCall(*) |
ast::ExprAssign(*) | ast::ExprAssignOp(*) |
ast::ExprFnBlock(*) | ast::ExprRet(*) |
ast::ExprFnBlock(*) | ast::ExprProc(*) | ast::ExprRet(*) |
ast::ExprDoBody(*) | ast::ExprUnary(*) |
ast::ExprMethodCall(*) | ast::ExprCast(*) | ast::ExprVstore(*) |
ast::ExprVec(*) | ast::ExprTup(*) | ast::ExprIf(*) |

View File

@ -555,7 +555,8 @@ impl VisitContext {
self.use_expr(base, comp_mode);
}
ExprFnBlock(ref decl, ref body) => {
ExprFnBlock(ref decl, ref body) |
ExprProc(ref decl, ref body) => {
for a in decl.inputs.iter() {
self.use_pat(a.pat);
}

View File

@ -5043,7 +5043,8 @@ impl Resolver {
visit::walk_expr(self, expr, ());
}
ExprFnBlock(ref fn_decl, ref block) => {
ExprFnBlock(ref fn_decl, ref block) |
ExprProc(ref fn_decl, ref block) => {
self.resolve_function(FunctionRibKind(expr.id, block.id),
Some(fn_decl),
NoTypeParameters,

View File

@ -569,7 +569,8 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
}
ast_map::node_expr(ref expr) => {
match expr.node {
ast::ExprFnBlock(ref fn_decl, ref top_level_block) => {
ast::ExprFnBlock(ref fn_decl, ref top_level_block) |
ast::ExprProc(ref fn_decl, ref top_level_block) => {
let name = format!("fn{}", token::gensym("fn"));
let name = token::str_to_ident(name);
(name, fn_decl,
@ -2579,7 +2580,8 @@ fn populate_scope_map(cx: &mut CrateContext,
}
}
ast::ExprFnBlock(ast::fn_decl { inputs: ref inputs, _ }, ref block) => {
ast::ExprFnBlock(ast::fn_decl { inputs: ref inputs, _ }, ref block) |
ast::ExprProc(ast::fn_decl { inputs: ref inputs, _ }, ref block) => {
do with_new_scope(cx, block.span, scope_stack, scope_map) |cx,
scope_stack,
scope_map| {

View File

@ -717,10 +717,11 @@ fn trans_rvalue_dps_unadjusted(bcx: @mut Block, expr: &ast::Expr,
ast::ExprVec(*) | ast::ExprRepeat(*) => {
return tvec::trans_fixed_vstore(bcx, expr, expr, dest);
}
ast::ExprFnBlock(ref decl, ref body) => {
ast::ExprFnBlock(ref decl, ref body) |
ast::ExprProc(ref decl, ref body) => {
let expr_ty = expr_ty(bcx, expr);
let sigil = ty::ty_closure_sigil(expr_ty);
debug!("translating fn_block {} with type {}",
debug!("translating block function {} with type {}",
expr_to_str(expr, tcx.sess.intr()),
expr_ty.repr(tcx));
return closure::trans_expr_fn(bcx, sigil, decl, body,

View File

@ -3263,6 +3263,7 @@ pub fn expr_kind(tcx: ctxt,
ast::ExprIf(*) |
ast::ExprMatch(*) |
ast::ExprFnBlock(*) |
ast::ExprProc(*) |
ast::ExprDoBody(*) |
ast::ExprBlock(*) |
ast::ExprRepeat(*) |

View File

@ -1408,6 +1408,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
for (i, arg) in args.iter().enumerate() {
let is_block = match arg.node {
ast::ExprFnBlock(*) |
ast::ExprProc(*) |
ast::ExprDoBody(*) => true,
_ => false
};
@ -2592,6 +2593,15 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
check_expr_fn(fcx, expr, None,
decl, body, Vanilla, expected);
}
ast::ExprProc(ref decl, ref body) => {
check_expr_fn(fcx,
expr,
Some(ast::OwnedSigil),
decl,
body,
Vanilla,
expected);
}
ast::ExprDoBody(b) => {
let expected_sty = unpack_expected(fcx,
expected,

View File

@ -427,7 +427,7 @@ fn visit_expr(rcx: &mut Rcx, expr: @ast::Expr) {
visit::walk_expr(rcx, expr, ());
}
ast::ExprFnBlock(*) => {
ast::ExprFnBlock(*) | ast::ExprProc(*) => {
check_expr_fn_block(rcx, expr);
}
@ -457,7 +457,7 @@ fn check_expr_fn_block(rcx: &mut Rcx,
expr: @ast::Expr) {
let tcx = rcx.fcx.tcx();
match expr.node {
ast::ExprFnBlock(_, ref body) => {
ast::ExprFnBlock(_, ref body) | ast::ExprProc(_, ref body) => {
let function_type = rcx.resolve_node_type(expr.id);
match ty::get(function_type).sty {
ty::ty_closure(
@ -1027,6 +1027,7 @@ pub mod guarantor {
ast::ExprIf(*) |
ast::ExprMatch(*) |
ast::ExprFnBlock(*) |
ast::ExprProc(*) |
ast::ExprDoBody(*) |
ast::ExprBlock(*) |
ast::ExprRepeat(*) |

View File

@ -245,7 +245,7 @@ fn visit_expr(e: @ast::Expr, wbcx: &mut WbCtxt) {
}
match e.node {
ast::ExprFnBlock(ref decl, _) => {
ast::ExprFnBlock(ref decl, _) | ast::ExprProc(ref decl, _) => {
for input in decl.inputs.iter() {
let _ = resolve_type_vars_for_node(wbcx, e.span, input.id);
}

View File

@ -337,7 +337,14 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str {
}
fn closure_to_str(cx: ctxt, cty: &ty::ClosureTy) -> ~str
{
let mut s = cty.sigil.to_str();
let is_proc =
(cty.sigil, cty.onceness) == (ast::OwnedSigil, ast::Once);
let mut s = if is_proc {
~""
} else {
cty.sigil.to_str()
};
match (cty.sigil, cty.region) {
(ast::ManagedSigil, ty::re_static) |
@ -356,15 +363,19 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str {
}
};
match cty.onceness {
ast::Many => {}
ast::Once => {
s.push_str(cty.onceness.to_str());
s.push_char(' ');
}
};
if is_proc {
s.push_str("proc");
} else {
match cty.onceness {
ast::Many => {}
ast::Once => {
s.push_str(cty.onceness.to_str());
s.push_char(' ');
}
};
s.push_str("fn");
s.push_str("fn");
}
if !cty.bounds.is_empty() {
s.push_str(":");

View File

@ -649,23 +649,25 @@ fn waitpid(pid: pid_t) -> int {
unsafe {
let proc = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD);
if proc.is_null() {
let process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
FALSE,
pid as DWORD);
if process.is_null() {
fail!("failure in OpenProcess: {}", os::last_os_error());
}
loop {
let mut status = 0;
if GetExitCodeProcess(proc, &mut status) == FALSE {
CloseHandle(proc);
if GetExitCodeProcess(process, &mut status) == FALSE {
CloseHandle(process);
fail!("failure in GetExitCodeProcess: {}", os::last_os_error());
}
if status != STILL_ACTIVE {
CloseHandle(proc);
CloseHandle(process);
return status as int;
}
if WaitForSingleObject(proc, INFINITE) == WAIT_FAILED {
CloseHandle(proc);
if WaitForSingleObject(process, INFINITE) == WAIT_FAILED {
CloseHandle(process);
fail!("failure in WaitForSingleObject: {}", os::last_os_error());
}
}

View File

@ -413,7 +413,7 @@ mod tests {
let pipe_out = os::pipe();
let pipe_err = os::pipe();
let mut proc = run::Process::new("cat", [], run::ProcessOptions {
let mut process = run::Process::new("cat", [], run::ProcessOptions {
dir: None,
env: None,
in_fd: Some(pipe_in.input),
@ -430,7 +430,7 @@ mod tests {
}
let actual = readclose(pipe_out.input);
readclose(pipe_err.input);
proc.finish();
process.finish();
assert_eq!(~"test", actual);
}

View File

@ -537,6 +537,7 @@ pub enum Expr_ {
ExprLoop(Block, Option<Ident>),
ExprMatch(@Expr, ~[Arm]),
ExprFnBlock(fn_decl, Block),
ExprProc(fn_decl, Block),
ExprDoBody(@Expr),
ExprBlock(Block),

View File

@ -786,6 +786,9 @@ pub fn noop_fold_expr<T:ast_fold>(e: @ast::Expr, folder: &T) -> @ast::Expr {
folder.fold_block(body)
)
}
ExprProc(ref decl, ref body) => {
ExprProc(fold_fn_decl(decl, folder), folder.fold_block(body))
}
ExprBlock(ref blk) => ExprBlock(folder.fold_block(blk)),
ExprAssign(el, er) => {
ExprAssign(folder.fold_expr(el), folder.fold_expr(er))

View File

@ -27,7 +27,7 @@ use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock};
use ast::{ExprBreak, ExprCall, ExprCast, ExprDoBody};
use ast::{ExprField, ExprFnBlock, ExprIf, ExprIndex};
use ast::{ExprLit, ExprLogLevel, ExprLoop, ExprMac};
use ast::{ExprMethodCall, ExprParen, ExprPath, ExprRepeat};
use ast::{ExprMethodCall, ExprParen, ExprPath, ExprProc, ExprRepeat};
use ast::{ExprRet, ExprSelf, ExprStruct, ExprTup, ExprUnary};
use ast::{ExprVec, ExprVstore, ExprVstoreMutBox};
use ast::{ExprVstoreSlice, ExprVstoreBox};
@ -814,6 +814,21 @@ impl Parser {
});
}
// Parses a procedure type (`proc`). The initial `proc` keyword must
// already have been parsed.
pub fn parse_proc_type(&self) -> ty_ {
let (decl, lifetimes) = self.parse_ty_fn_decl();
ty_closure(@TyClosure {
sigil: OwnedSigil,
region: None,
purity: impure_fn,
onceness: Once,
bounds: None,
decl: decl,
lifetimes: lifetimes,
})
}
// parse a ty_closure type
pub fn parse_ty_closure(&self,
sigil: ast::Sigil,
@ -1123,6 +1138,8 @@ impl Parser {
let e = self.parse_expr();
self.expect(&token::RPAREN);
ty_typeof(e)
} else if self.eat_keyword(keywords::Proc) {
self.parse_proc_type()
} else if *self.token == token::MOD_SEP
|| is_ident_or_path(self.token) {
// NAMED TYPE
@ -1672,6 +1689,19 @@ impl Parser {
ExprBlock(blk));
} else if token::is_bar(&*self.token) {
return self.parse_lambda_expr();
} else if self.eat_keyword(keywords::Proc) {
let decl = self.parse_proc_decl();
let body = self.parse_expr();
let fakeblock = ast::Block {
view_items: ~[],
stmts: ~[],
expr: Some(body),
id: ast::DUMMY_NODE_ID,
rules: DefaultBlock,
span: body.span,
};
return self.mk_expr(lo, body.span.hi, ExprProc(decl, fakeblock));
} else if self.eat_keyword(keywords::Self) {
ex = ExprSelf;
hi = self.span.hi;
@ -3616,6 +3646,31 @@ impl Parser {
}
}
// Parses the `(arg, arg) -> return_type` header on a procedure.
fn parse_proc_decl(&self) -> fn_decl {
let inputs =
self.parse_unspanned_seq(&token::LPAREN,
&token::RPAREN,
seq_sep_trailing_allowed(token::COMMA),
|p| p.parse_fn_block_arg());
let output = if self.eat(&token::RARROW) {
self.parse_ty(false)
} else {
Ty {
id: ast::DUMMY_NODE_ID,
node: ty_infer,
span: *self.span,
}
};
ast::fn_decl {
inputs: inputs,
output: output,
cf: return_val,
}
}
// parse the name and optional generic types of a function header.
fn parse_fn_header(&self) -> (Ident, ast::Generics) {
let id = self.parse_ident();

View File

@ -486,14 +486,15 @@ fn mk_fresh_ident_interner() -> @ident_interner {
"while", // 62
"in", // 63
"continue", // 64
"proc", // 65
"be", // 65
"pure", // 66
"yield", // 67
"typeof", // 68
"alignof", // 69
"offsetof", // 70
"sizeof", // 71
"be", // 66
"pure", // 67
"yield", // 68
"typeof", // 69
"alignof", // 70
"offsetof", // 71
"sizeof", // 72
];
@interner::StrInterner::prefill(init_vec)
@ -502,9 +503,9 @@ fn mk_fresh_ident_interner() -> @ident_interner {
static SELF_KEYWORD_NAME: uint = 8;
static STATIC_KEYWORD_NAME: uint = 27;
static STRICT_KEYWORD_START: uint = 32;
static STRICT_KEYWORD_FINAL: uint = 64;
static RESERVED_KEYWORD_START: uint = 65;
static RESERVED_KEYWORD_FINAL: uint = 71;
static STRICT_KEYWORD_FINAL: uint = 65;
static RESERVED_KEYWORD_START: uint = 66;
static RESERVED_KEYWORD_FINAL: uint = 72;
// if an interner exists in TLS, return it. Otherwise, prepare a
// fresh one.
@ -645,6 +646,7 @@ pub mod keywords {
Use,
While,
Continue,
Proc,
// Reserved keywords
Alignof,
@ -694,14 +696,15 @@ pub mod keywords {
Use => Ident { name: 61, ctxt: 0 },
While => Ident { name: 62, ctxt: 0 },
Continue => Ident { name: 64, ctxt: 0 },
Proc => Ident { name: 65, ctxt: 0 },
Alignof => Ident { name: 69, ctxt: 0 },
Be => Ident { name: 65, ctxt: 0 },
Offsetof => Ident { name: 70, ctxt: 0 },
Pure => Ident { name: 66, ctxt: 0 },
Sizeof => Ident { name: 71, ctxt: 0 },
Typeof => Ident { name: 68, ctxt: 0 },
Yield => Ident { name: 67, ctxt: 0 },
Alignof => Ident { name: 70, ctxt: 0 },
Be => Ident { name: 66, ctxt: 0 },
Offsetof => Ident { name: 71, ctxt: 0 },
Pure => Ident { name: 67, ctxt: 0 },
Sizeof => Ident { name: 72, ctxt: 0 },
Typeof => Ident { name: 69, ctxt: 0 },
Yield => Ident { name: 68, ctxt: 0 },
}
}
}

View File

@ -1350,6 +1350,33 @@ pub fn print_expr(s: @ps, expr: &ast::Expr) {
// empty box to satisfy the close.
ibox(s, 0);
}
ast::ExprProc(ref decl, ref body) => {
// in do/for blocks we don't want to show an empty
// argument list, but at this point we don't know which
// we are inside.
//
// if !decl.inputs.is_empty() {
print_proc_args(s, decl);
space(s.s);
// }
assert!(body.stmts.is_empty());
assert!(body.expr.is_some());
// we extract the block, so as not to create another set of boxes
match body.expr.unwrap().node {
ast::ExprBlock(ref blk) => {
print_block_unclosed(s, blk);
}
_ => {
// this is a bare expression
print_expr(s, body.expr.unwrap());
end(s); // need to close a box
}
}
// a box will be closed by print_expr, but we didn't want an overall
// wrapper so we closed the corresponding opening. so create an
// empty box to satisfy the close.
ibox(s, 0);
}
ast::ExprDoBody(body) => {
print_expr(s, body);
}
@ -1777,6 +1804,24 @@ pub fn print_fn_block_args(s: @ps, decl: &ast::fn_decl) {
maybe_print_comment(s, decl.output.span.lo);
}
pub fn print_proc_args(s: @ps, decl: &ast::fn_decl) {
word(s.s, "proc");
word(s.s, "(");
print_fn_args(s, decl, None);
word(s.s, ")");
match decl.output.node {
ast::ty_infer => {}
_ => {
space_if_not_bol(s);
word_space(s, "->");
print_type(s, &decl.output);
}
}
maybe_print_comment(s, decl.output.span.lo);
}
pub fn print_bounds(s: @ps, bounds: &OptVec<ast::TyParamBound>,
print_colon_anyway: bool) {
if !bounds.is_empty() {
@ -1968,12 +2013,16 @@ pub fn print_ty_fn(s: @ps,
// Duplicates the logic in `print_fn_header_info()`. This is because that
// function prints the sigil in the wrong place. That should be fixed.
print_extern_opt_abis(s, opt_abis);
print_opt_sigil(s, opt_sigil);
print_opt_lifetime(s, opt_region);
print_purity(s, purity);
print_onceness(s, onceness);
word(s.s, "fn");
if opt_sigil == Some(ast::OwnedSigil) && onceness == ast::Once {
word(s.s, "proc");
} else {
print_extern_opt_abis(s, opt_abis);
print_opt_sigil(s, opt_sigil);
print_opt_lifetime(s, opt_region);
print_purity(s, purity);
print_onceness(s, onceness);
word(s.s, "fn");
}
match id { Some(id) => { word(s.s, " "); print_ident(s, id); } _ => () }
do opt_bounds.as_ref().map |bounds| { print_bounds(s, bounds, true); };
match generics { Some(g) => print_generics(s, g), _ => () }

View File

@ -560,6 +560,14 @@ pub fn walk_expr<E:Clone, V:Visitor<E>>(visitor: &mut V, expression: @Expr, env:
expression.id,
env.clone())
}
ExprProc(ref function_declaration, ref body) => {
visitor.visit_fn(&fk_fn_block,
function_declaration,
body,
expression.span,
expression.id,
env.clone())
}
ExprBlock(ref block) => visitor.visit_block(block, env.clone()),
ExprAssign(left_hand_expression, right_hand_expression) => {
visitor.visit_expr(right_hand_expression, env.clone());

View File

@ -0,0 +1,27 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
fn call_it(f: proc(~str) -> ~str) {
println(f(~"Fred"))
}
pub fn main() {
let greeting = ~"Hi ";
do call_it |s| {
greeting + s
}
let greeting = ~"Hello ";
call_it(proc(s) {
greeting + s
});
let greeting = ~"Goodbye ";
call_it(proc(s) greeting + s);
let greeting = ~"How's life, ";
call_it(proc(s: ~str) -> ~str {
greeting + s
});
}

View File

@ -64,14 +64,14 @@ fn test_destroy_actually_kills(force: bool) {
use std::libc::consts::os::extra::{FALSE, PROCESS_QUERY_INFORMATION, STILL_ACTIVE };
unsafe {
let proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD);
if proc.is_null() {
let process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD);
if process.is_null() {
return false;
}
// proc will be non-null if the process is alive, or if it died recently
// process will be non-null if the process is alive, or if it died recently
let mut status = 0;
GetExitCodeProcess(proc, &mut status);
CloseHandle(proc);
GetExitCodeProcess(process, &mut status);
CloseHandle(process);
return status == STILL_ACTIVE;
}
}