rustc: Implement parsing and typechecking for "once fn"

This commit is contained in:
Patrick Walton
2012-11-02 13:33:51 -07:00
parent 9aadfc3f4b
commit be93b29d30
22 changed files with 266 additions and 61 deletions

View File

@@ -387,6 +387,14 @@ fn parse_purity(c: char) -> purity {
}
}
fn parse_onceness(c: char) -> ast::Onceness {
match c {
'o' => ast::Once,
'm' => ast::Many,
_ => fail ~"parse_onceness: bad onceness"
}
}
fn parse_arg(st: @pstate, conv: conv_did) -> ty::arg {
{mode: parse_mode(st),
ty: parse_ty(st, conv)}
@@ -406,6 +414,7 @@ fn parse_mode(st: @pstate) -> ast::mode {
fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::FnTy {
let proto = parse_proto(st);
let purity = parse_purity(next(st));
let onceness = parse_onceness(next(st));
let bounds = parse_bounds(st, conv);
assert (next(st) == '[');
let mut inputs: ~[ty::arg] = ~[];
@@ -418,6 +427,7 @@ fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::FnTy {
return FnTyBase {
meta: FnMeta {purity: purity,
proto: proto,
onceness: onceness,
bounds: bounds,
ret_style: ret_style},
sig: FnSig {inputs: inputs,

View File

@@ -349,9 +349,17 @@ fn enc_purity(w: io::Writer, p: purity) {
}
}
fn enc_onceness(w: io::Writer, o: Onceness) {
match o {
Once => w.write_char('o'),
Many => w.write_char('m')
}
}
fn enc_ty_fn(w: io::Writer, cx: @ctxt, ft: ty::FnTy) {
enc_proto(w, cx, ft.meta.proto);
enc_purity(w, ft.meta.purity);
enc_onceness(w, ft.meta.onceness);
enc_bounds(w, cx, ft.meta.bounds);
w.write_char('[');
for ft.sig.inputs.each |arg| {

View File

@@ -841,7 +841,7 @@ fn check_fn_deprecated_modes(tcx: ty::ctxt, fn_ty: ty::t, decl: ast::fn_decl,
let span = arg_ast.ty.span;
// Recurse to check fn-type argument
match arg_ast.ty.node {
ast::ty_fn(_, _, _, decl) => {
ast::ty_fn(_, _, _, _, decl) => {
check_fn_deprecated_modes(tcx, arg_ty.ty,
decl, span, id);
}
@@ -856,7 +856,7 @@ fn check_fn_deprecated_modes(tcx: ty::ctxt, fn_ty: ty::t, decl: ast::fn_decl,
// Functions with preceding sigil are parsed
// as pointers of functions
match mt.ty.node {
ast::ty_fn(_, _, _, decl) => {
ast::ty_fn(_, _, _, _, decl) => {
check_fn_deprecated_modes(
tcx, arg_ty.ty,
decl, span, id);
@@ -889,7 +889,7 @@ fn check_item_deprecated_modes(tcx: ty::ctxt, it: @ast::item) {
match it.node {
ast::item_ty(ty, _) => {
match ty.node {
ast::ty_fn(_, _, _, decl) => {
ast::ty_fn(_, _, _, _, decl) => {
let fn_ty = ty::node_id_to_type(tcx, it.id);
check_fn_deprecated_modes(
tcx, fn_ty, decl, ty.span, it.id)

View File

@@ -624,8 +624,8 @@ fn determine_rp_in_ty(ty: @ast::Ty,
}
}
ast::ty_fn(ast::proto_bare, _, _, _) |
ast::ty_fn(ast::proto_block, _, _, _) if cx.anon_implies_rp => {
ast::ty_fn(ast::proto_bare, _, _, _, _) |
ast::ty_fn(ast::proto_block, _, _, _, _) if cx.anon_implies_rp => {
debug!("referenced bare fn type with regions %s",
pprust::ty_to_str(ty, cx.sess.intr()));
cx.add_rp(cx.item_id, cx.add_variance(rv_contravariant));
@@ -672,8 +672,8 @@ fn determine_rp_in_ty(ty: @ast::Ty,
match ty.node {
ast::ty_box(mt) | ast::ty_uniq(mt) => {
match mt.ty.node {
ast::ty_fn(ast::proto_bare, _, _, _) |
ast::ty_fn(ast::proto_block, _, _, _) => {
ast::ty_fn(ast::proto_bare, _, _, _, _) |
ast::ty_fn(ast::proto_block, _, _, _, _) => {
do cx.with(cx.item_id, false) {
visit_mt(mt, cx, visitor);
}
@@ -706,7 +706,7 @@ fn determine_rp_in_ty(ty: @ast::Ty,
}
}
ast::ty_fn(_, _, bounds, decl) => {
ast::ty_fn(_, _, _, bounds, decl) => {
// fn() binds the & region, so do not consider &T types that
// appear *inside* a fn() type to affect the enclosing item:
do cx.with(cx.item_id, false) {

View File

@@ -1000,6 +1000,7 @@ fn trans_intrinsic(ccx: @crate_ctxt, decl: ValueRef, item: @ast::foreign_item,
proto:
ty::proto_vstore(ty::vstore_slice(
ty::re_bound(ty::br_anon(0)))),
onceness: ast::Many,
bounds: @~[],
ret_style: ast::return_val},
sig: FnSig {inputs: ~[{mode: ast::expl(ast::by_val),

View File

@@ -250,6 +250,7 @@ fn normalize_for_monomorphization(tcx: ty::ctxt, ty: ty::t) -> Option<ty::t> {
tcx,
FnTyBase {meta: FnMeta {purity: ast::impure_fn,
proto: fty.meta.proto,
onceness: ast::Many,
bounds: @~[],
ret_style: ast::return_val},
sig: FnSig {inputs: ~[],
@@ -261,6 +262,7 @@ fn normalize_for_monomorphization(tcx: ty::ctxt, ty: ty::t) -> Option<ty::t> {
tcx,
FnTyBase {meta: FnMeta {purity: ast::impure_fn,
proto: box_proto,
onceness: ast::Many,
bounds: @~[],
ret_style: ast::return_val},
sig: FnSig {inputs: ~[],

View File

@@ -126,6 +126,7 @@ export kind_is_owned;
export meta_kind, kind_lteq, type_kind;
export operators;
export type_err, terr_vstore_kind;
export terr_onceness_mismatch;
export type_err_to_str, note_and_explain_type_err;
export expected_found;
export type_needs_drop;
@@ -186,6 +187,7 @@ export terr_proto_mismatch;
export terr_ret_style_mismatch;
export terr_fn, terr_trait;
export purity_to_str;
export onceness_to_str;
export param_tys_in_type;
export eval_repeat_count;
export fn_proto, proto_bare, proto_vstore;
@@ -504,11 +506,14 @@ impl fn_proto : cmp::Eq {
*
* - `purity` is the function's effect (pure, impure, unsafe).
* - `proto` is the protocol (fn@, fn~, etc).
* - `onceness` indicates whether the function can be called one time or many
* times.
* - `bounds` is the parameter bounds on the function's upvars.
* - `ret_style` indicates whether the function returns a value or fails. */
struct FnMeta {
purity: ast::purity,
proto: fn_proto,
onceness: ast::Onceness,
bounds: @~[param_bound],
ret_style: ret_style
}
@@ -679,6 +684,7 @@ enum type_err {
terr_mismatch,
terr_ret_style_mismatch(expected_found<ast::ret_style>),
terr_purity_mismatch(expected_found<purity>),
terr_onceness_mismatch(expected_found<Onceness>),
terr_mutability,
terr_proto_mismatch(expected_found<ty::fn_proto>),
terr_box_mutability,
@@ -3326,6 +3332,11 @@ fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str {
purity_to_str(values.expected),
purity_to_str(values.found))
}
terr_onceness_mismatch(values) => {
fmt!("expected %s fn but found %s fn",
onceness_to_str(values.expected),
onceness_to_str(values.found))
}
terr_proto_mismatch(values) => {
fmt!("expected %s closure, found %s closure",
proto_ty_to_str(cx, values.expected),

View File

@@ -208,7 +208,8 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope Copy Owned>(
_ => ()
}
}
ast::ty_fn(ast::proto_block, purity, ast_bounds, ast_fn_decl) => {
ast::ty_fn(ast::proto_block, purity, onceness, ast_bounds,
ast_fn_decl) => {
let new_proto;
match vst {
ty::vstore_fixed(_) => {
@@ -223,9 +224,15 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope Copy Owned>(
// Run through the normal function type conversion process.
let bounds = collect::compute_bounds(self.ccx(), ast_bounds);
let fn_decl = ty_of_fn_decl(self, rscope, new_proto, purity,
let fn_decl = ty_of_fn_decl(self,
rscope,
new_proto,
purity,
onceness,
bounds,
ast_fn_decl, None, span);
ast_fn_decl,
None,
span);
return ty::mk_fn(tcx, fn_decl);
}
_ => ()
@@ -309,10 +316,10 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope Copy Owned>(
};
ty::mk_rec(tcx, flds)
}
ast::ty_fn(proto, purity, ast_bounds, decl) => {
ast::ty_fn(proto, purity, onceness, ast_bounds, decl) => {
let bounds = collect::compute_bounds(self.ccx(), ast_bounds);
let fn_decl = ty_of_fn_decl(self, rscope, proto, purity,
bounds, decl, None,
onceness, bounds, decl, None,
ast_ty.span);
ty::mk_fn(tcx, fn_decl)
}
@@ -476,6 +483,7 @@ fn ty_of_fn_decl<AC: ast_conv, RS: region_scope Copy Owned>(
self: AC, rscope: RS,
ast_proto: ast::proto,
purity: ast::purity,
onceness: ast::Onceness,
bounds: @~[ty::param_bound],
decl: ast::fn_decl,
expected_tys: expected_tys,
@@ -508,6 +516,7 @@ fn ty_of_fn_decl<AC: ast_conv, RS: region_scope Copy Owned>(
FnTyBase {
meta: FnMeta {purity: purity,
proto: proto,
onceness: onceness,
bounds: bounds,
ret_style: decl.cf},
sig: FnSig {inputs: input_tys,

View File

@@ -1310,7 +1310,10 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// block syntax lambdas; that is, lambdas without explicit
// protos.
let expected_sty = unpack_expected(fcx, expected, |x| Some(x));
let (expected_tys, expected_purity, expected_proto) =
let (expected_tys,
expected_purity,
expected_proto,
expected_onceness) =
match expected_sty {
Some(ty::ty_fn(ref fn_ty)) => {
let {fn_ty, _} =
@@ -1320,10 +1323,14 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
(Some({inputs: fn_ty.sig.inputs,
output: fn_ty.sig.output}),
fn_ty.meta.purity,
fn_ty.meta.proto)
fn_ty.meta.proto,
fn_ty.meta.onceness)
}
_ => {
(None, ast::impure_fn, ty::proto_vstore(ty::vstore_box))
(None,
ast::impure_fn,
ty::proto_vstore(ty::vstore_box),
ast::Many)
}
};
@@ -1334,17 +1341,25 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// XXX: This is a hack.
let ast_proto = ast_proto_opt.get_default(ast::proto_box);
let ast_purity = ast::impure_fn;
let ast_onceness = ast::Many;
// construct the function type
let mut fn_ty = astconv::ty_of_fn_decl(fcx, fcx,
ast_proto, ast_purity, @~[],
decl, expected_tys, expr.span);
let mut fn_ty = astconv::ty_of_fn_decl(fcx,
fcx,
ast_proto,
ast_purity,
ast_onceness,
@~[],
decl,
expected_tys,
expr.span);
// Patch up the function declaration, if necessary.
match ast_proto_opt {
None => {
fn_ty.meta.purity = expected_purity;
fn_ty.meta.proto = expected_proto;
fn_ty.meta.onceness = expected_onceness;
}
Some(_) => { }
}
@@ -2802,6 +2817,7 @@ fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) {
meta: FnMeta {purity: ast::impure_fn,
proto: ty::proto_vstore(ty::vstore_slice(
ty::re_bound(ty::br_anon(0)))),
onceness: ast::Many,
bounds: @~[],
ret_style: ast::return_val},
sig: FnSig {inputs: ~[{mode: ast::expl(ast::by_val),
@@ -2825,6 +2841,7 @@ fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) {
let fty = ty::mk_fn(tcx, FnTyBase {
meta: FnMeta {purity: ast::impure_fn,
proto: ty::proto_bare,
onceness: ast::Many,
bounds: @~[],
ret_style: ast::return_val},
sig: FnSig {inputs: inputs,

View File

@@ -134,6 +134,7 @@ fn get_enum_variant_types(ccx: @crate_ctxt,
result_ty = Some(ty::mk_fn(tcx, FnTyBase {
meta: FnMeta {purity: ast::pure_fn,
proto: ty::proto_vstore(ty::vstore_box),
onceness: ast::Many,
bounds: @~[],
ret_style: ast::return_val},
sig: FnSig {inputs: args,
@@ -604,7 +605,7 @@ fn convert_struct(ccx: @crate_ctxt,
let t_dtor = ty::mk_fn(
tcx,
ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare,
ast::impure_fn, @~[],
ast::impure_fn, ast::Many, @~[],
ast_util::dtor_dec(), None, dtor.span));
write_ty_to_tcx(tcx, dtor.node.id, t_dtor);
tcx.tcache.insert(local_def(dtor.node.id),
@@ -643,6 +644,7 @@ fn convert_struct(ccx: @crate_ctxt,
meta: FnMeta {
purity: ast::pure_fn,
proto: ty::proto_bare,
onceness: ast::Many,
bounds: @~[],
ret_style: ast::return_val,
},
@@ -682,9 +684,15 @@ fn ty_of_method(ccx: @crate_ctxt,
rp: Option<ty::region_variance>) -> ty::method {
{ident: m.ident,
tps: ty_param_bounds(ccx, m.tps),
fty: ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare,
m.purity, @~[],
m.decl, None, m.span),
fty: ty_of_fn_decl(ccx,
type_rscope(rp),
ast::proto_bare,
m.purity,
ast::Many,
@~[],
m.decl,
None,
m.span),
self_ty: m.self_ty.node,
vis: m.vis,
def_id: local_def(m.id)}
@@ -696,8 +704,15 @@ fn ty_of_ty_method(self: @crate_ctxt,
id: ast::def_id) -> ty::method {
{ident: m.ident,
tps: ty_param_bounds(self, m.tps),
fty: ty_of_fn_decl(self, type_rscope(rp), ast::proto_bare, m.purity,
@~[], m.decl, None, m.span),
fty: ty_of_fn_decl(self,
type_rscope(rp),
ast::proto_bare,
m.purity,
ast::Many,
@~[],
m.decl,
None,
m.span),
// assume public, because this is only invoked on trait methods
self_ty: m.self_ty.node,
vis: ast::public,
@@ -752,9 +767,15 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item)
}
ast::item_fn(decl, purity, tps, _) => {
let bounds = ty_param_bounds(ccx, tps);
let tofd = ty_of_fn_decl(ccx, empty_rscope,
ast::proto_bare, purity, @~[],
decl, None, it.span);
let tofd = ty_of_fn_decl(ccx,
empty_rscope,
ast::proto_bare,
purity,
ast::Many,
@~[],
decl,
None,
it.span);
let tpt = {bounds: bounds,
region_param: None,
ty: ty::mk_fn(ccx.tcx, tofd)};
@@ -910,6 +931,7 @@ fn ty_of_foreign_fn_decl(ccx: @crate_ctxt,
let t_fn = ty::mk_fn(ccx.tcx, FnTyBase {
meta: FnMeta {purity: purity,
proto: ty::proto_bare,
onceness: ast::Many,
bounds: @~[],
ret_style: ast::return_val},
sig: FnSig {inputs: input_tys,

View File

@@ -46,6 +46,7 @@
use to_str::ToStr;
use ty::{FnTyBase, FnMeta, FnSig};
use syntax::ast::Onceness;
trait combine {
fn infcx() -> infer_ctxt;
@@ -72,6 +73,7 @@ trait combine {
fn protos(p1: ty::fn_proto, p2: ty::fn_proto) -> cres<ty::fn_proto>;
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style>;
fn purities(a: purity, b: purity) -> cres<purity>;
fn oncenesses(a: Onceness, b: Onceness) -> cres<Onceness>;
fn contraregions(a: ty::Region, b: ty::Region) -> cres<ty::Region>;
fn regions(a: ty::Region, b: ty::Region) -> cres<ty::Region>;
fn vstores(vk: ty::terr_vstore_kind,
@@ -311,10 +313,14 @@ fn super_fn_metas<C:combine>(
do self.protos(a_f.proto, b_f.proto).chain |p| {
do self.ret_styles(a_f.ret_style, b_f.ret_style).chain |rs| {
do self.purities(a_f.purity, b_f.purity).chain |purity| {
Ok(FnMeta {purity: purity,
proto: p,
bounds: a_f.bounds, // XXX: This is wrong!
ret_style: rs})
do self.oncenesses(a_f.onceness, b_f.onceness).chain
|onceness| {
Ok(FnMeta {purity: purity,
proto: p,
onceness: onceness,
bounds: a_f.bounds, // XXX: This is wrong!
ret_style: rs})
}
}
}
}

View File

@@ -1,6 +1,7 @@
use combine::*;
use lattice::*;
use to_str::ToStr;
use syntax::ast::{Many, Once};
enum Glb = combine_fields; // "greatest lower bound" (common subtype)
@@ -97,6 +98,13 @@ impl Glb: combine {
}
}
fn oncenesses(a: Onceness, b: Onceness) -> cres<Onceness> {
match (a, b) {
(Many, _) | (_, Many) => Ok(Many),
(Once, Once) => Ok(Once)
}
}
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style> {
match (r1, r2) {
(ast::return_val, ast::return_val) => {

View File

@@ -1,6 +1,7 @@
use combine::*;
use lattice::*;
use to_str::ToStr;
use syntax::ast::{Many, Once};
enum Lub = combine_fields; // "subtype", "subregion" etc
@@ -80,6 +81,13 @@ impl Lub: combine {
}
}
fn oncenesses(a: Onceness, b: Onceness) -> cres<Onceness> {
match (a, b) {
(Once, _) | (_, Once) => Ok(Once),
(Many, Many) => Ok(Many)
}
}
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style> {
match (r1, r2) {
(ast::return_val, _) |

View File

@@ -93,6 +93,12 @@ impl Sub: combine {
})
}
fn oncenesses(a: Onceness, b: Onceness) -> cres<Onceness> {
self.lub().oncenesses(a, b).compare(b, || {
ty::terr_onceness_mismatch(expected_found(&self, a, b))
})
}
fn ret_styles(a: ret_style, b: ret_style) -> cres<ret_style> {
self.lub().ret_styles(a, b).compare(b, || {
ty::terr_ret_style_mismatch(expected_found(&self, a, b))

View File

@@ -19,7 +19,8 @@ use syntax::codemap;
use syntax::codemap::span;
use syntax::print::pprust;
use syntax::print::pprust::{path_to_str, proto_to_str,
mode_to_str, purity_to_str};
mode_to_str, purity_to_str,
onceness_to_str};
use syntax::{ast, ast_util};
use syntax::ast_map;
@@ -266,14 +267,24 @@ fn ty_to_str(cx: ctxt, typ: t) -> ~str {
};
modestr + ty_to_str(cx, ty)
}
fn fn_to_str(cx: ctxt, purity: ast::purity, proto: ty::fn_proto,
fn fn_to_str(cx: ctxt,
purity: ast::purity,
proto: ty::fn_proto,
onceness: ast::Onceness,
ident: Option<ast::ident>,
inputs: ~[arg], output: t, cf: ast::ret_style) -> ~str {
inputs: ~[arg],
output: t,
cf: ast::ret_style) -> ~str {
let mut s;
s = match purity {
ast::impure_fn => ~"",
_ => purity_to_str(purity) + ~" "
ast::impure_fn => ~"",
_ => purity_to_str(purity) + ~" "
};
s += match onceness {
ast::Many => ~"",
ast::Once => onceness_to_str(onceness) + ~" "
};
s += ~"fn";
@@ -298,8 +309,13 @@ fn ty_to_str(cx: ctxt, typ: t) -> ~str {
}
fn method_to_str(cx: ctxt, m: method) -> ~str {
return fn_to_str(
cx, m.fty.meta.purity, m.fty.meta.proto, Some(m.ident),
m.fty.sig.inputs, m.fty.sig.output,
cx,
m.fty.meta.purity,
m.fty.meta.proto,
m.fty.meta.onceness,
Some(m.ident),
m.fty.sig.inputs,
m.fty.sig.output,
m.fty.meta.ret_style) + ~";";
}
fn field_to_str(cx: ctxt, f: field) -> ~str {
@@ -347,8 +363,14 @@ fn ty_to_str(cx: ctxt, typ: t) -> ~str {
~"(" + str::connect(strs, ~",") + ~")"
}
ty_fn(ref f) => {
fn_to_str(cx, f.meta.purity, f.meta.proto, None, f.sig.inputs,
f.sig.output, f.meta.ret_style)
fn_to_str(cx,
f.meta.purity,
f.meta.proto,
f.meta.onceness,
None,
f.sig.inputs,
f.sig.output,
f.meta.ret_style)
}
ty_infer(infer_ty) => infer_ty.to_str(),
ty_param({idx: id, _}) => {