Skip to content

Commit 2e53da5

Browse files
committed
Typecheck function preconditions
It turned out that function preconditions weren't getting checked at all, so you could write a constraint on a fn decl that was total nonsense. Fixed now.
1 parent f62add9 commit 2e53da5

File tree

1 file changed

+107
-50
lines changed

1 file changed

+107
-50
lines changed

src/comp/middle/typeck.rs

Lines changed: 107 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -979,9 +979,9 @@ mod demand {
979979
// Requires that the two types unify, and prints an error message if they
980980
// don't. Returns the unified type and the type parameter substitutions.
981981
fn full(fcx: &@fn_ctxt, sp: &span, expected: ty::t, actual: ty::t,
982-
ty_param_substs_0: &[ty::t], do_block_coerece: bool) ->
982+
ty_param_substs_0: &[ty::t], do_block_coerce: bool) ->
983983
ty_param_substs_and_ty {
984-
if do_block_coerece {
984+
if do_block_coerce {
985985
actual = do_fn_block_coerce(fcx, sp, actual, expected);
986986
}
987987
@@ -1715,54 +1715,6 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr, unify: &unifier,
17151715
ret bot;
17161716
}
17171717

1718-
// A generic function for checking the pred in a check
1719-
// or if-check
1720-
fn check_pred_expr(fcx: &@fn_ctxt, e: &@ast::expr) -> bool {
1721-
let bot = check_expr_with(fcx, e, ty::mk_bool(fcx.ccx.tcx));
1722-
1723-
/* e must be a call expr where all arguments are either
1724-
literals or slots */
1725-
alt e.node {
1726-
ast::expr_call(operator, operands) {
1727-
if !ty::is_pred_ty(fcx.ccx.tcx, expr_ty(fcx.ccx.tcx, operator)) {
1728-
fcx.ccx.tcx.sess.span_fatal(operator.span,
1729-
~"Operator in constraint has non-boolean return type");
1730-
}
1731-
1732-
alt operator.node {
1733-
ast::expr_path(oper_name) {
1734-
alt fcx.ccx.tcx.def_map.find(operator.id) {
1735-
some(ast::def_fn(_, ast::pure_fn.)) {
1736-
// do nothing
1737-
}
1738-
_ {
1739-
fcx.ccx.tcx.sess.span_fatal(operator.span,
1740-
~"Impure function as operator \
1741-
in constraint");
1742-
}
1743-
}
1744-
for operand: @ast::expr in operands {
1745-
if !ast_util::is_constraint_arg(operand) {
1746-
let s =
1747-
~"Constraint args must be \
1748-
slot variables or literals";
1749-
fcx.ccx.tcx.sess.span_fatal(e.span, s);
1750-
}
1751-
}
1752-
}
1753-
_ {
1754-
let s =
1755-
~"In a constraint, expected the \
1756-
constraint name to be an explicit name";
1757-
fcx.ccx.tcx.sess.span_fatal(e.span, s);
1758-
}
1759-
}
1760-
}
1761-
_ { fcx.ccx.tcx.sess.span_fatal(
1762-
e.span, ~"check on non-predicate"); }
1763-
}
1764-
ret bot;
1765-
}
17661718

17671719
// A generic function for checking the then and else in an if
17681720
// or if-check
@@ -2604,6 +2556,110 @@ fn check_const(ccx: &@crate_ctxt, _sp: &span, e: &@ast::expr,
26042556
check_expr(fcx, e);
26052557
}
26062558

2559+
// A generic function for checking the pred in a check
2560+
// or if-check
2561+
fn check_pred_expr(fcx: &@fn_ctxt, e: &@ast::expr) -> bool {
2562+
let bot = check_expr_with(fcx, e, ty::mk_bool(fcx.ccx.tcx));
2563+
2564+
/* e must be a call expr where all arguments are either
2565+
literals or slots */
2566+
alt e.node {
2567+
ast::expr_call(operator, operands) {
2568+
if !ty::is_pred_ty(fcx.ccx.tcx, expr_ty(fcx.ccx.tcx, operator)) {
2569+
fcx.ccx.tcx.sess.span_fatal(operator.span,
2570+
~"Operator in constraint has non-boolean return type");
2571+
}
2572+
2573+
alt operator.node {
2574+
ast::expr_path(oper_name) {
2575+
alt fcx.ccx.tcx.def_map.find(operator.id) {
2576+
some(ast::def_fn(_, ast::pure_fn.)) {
2577+
// do nothing
2578+
}
2579+
_ {
2580+
fcx.ccx.tcx.sess.span_fatal(operator.span,
2581+
~"Impure function as operator \
2582+
in constraint");
2583+
}
2584+
}
2585+
for operand: @ast::expr in operands {
2586+
if !ast_util::is_constraint_arg(operand) {
2587+
let s =
2588+
~"Constraint args must be \
2589+
slot variables or literals";
2590+
fcx.ccx.tcx.sess.span_fatal(e.span, s);
2591+
}
2592+
}
2593+
}
2594+
_ {
2595+
let s =
2596+
~"In a constraint, expected the \
2597+
constraint name to be an explicit name";
2598+
fcx.ccx.tcx.sess.span_fatal(e.span, s);
2599+
}
2600+
}
2601+
}
2602+
_ { fcx.ccx.tcx.sess.span_fatal(
2603+
e.span, ~"check on non-predicate"); }
2604+
}
2605+
ret bot;
2606+
}
2607+
2608+
fn check_constraints(fcx: &@fn_ctxt, cs: [@ast::constr], args:[ast::arg]) {
2609+
let c_args;
2610+
let num_args = vec::len(args);
2611+
for c: @ast::constr in cs {
2612+
c_args = [];
2613+
for a: @spanned<ast::fn_constr_arg> in c.node.args {
2614+
c_args += [@(alt a.node {
2615+
ast::carg_base. {
2616+
// "base" should not occur in a fn type thing, as of
2617+
// yet, b/c we don't allow constraints on the return type
2618+
2619+
fcx.ccx.tcx.sess.span_bug(a.span, ~"check_constraints:\
2620+
unexpected carg_base");
2621+
}
2622+
ast::carg_lit(l) {
2623+
let tmp_node_id = fcx.ccx.tcx.sess.next_node_id();
2624+
{id:tmp_node_id, node: ast::expr_lit(l), span:a.span} }
2625+
ast::carg_ident(i) {
2626+
if i < num_args {
2627+
let p : ast::path_ =
2628+
{global:false, idents:[(args[i]).ident],
2629+
// Works b/c no higher-order polymorphism
2630+
types:[]};
2631+
/*
2632+
This is kludgy, and we probably shouldn't be assigning
2633+
node IDs here, but we're creating exprs that are
2634+
ephemeral, just for the purposes of typechecking. So
2635+
that's my justification.
2636+
*/
2637+
let arg_occ_node_id = fcx.ccx.tcx.sess.next_node_id();
2638+
fcx.ccx.tcx.def_map.insert(arg_occ_node_id,
2639+
ast::def_arg(local_def(args[i].id)));
2640+
{id:arg_occ_node_id,
2641+
node: ast::expr_path(respan(a.span, p)),
2642+
span:a.span}
2643+
}
2644+
else {
2645+
fcx.ccx.tcx.sess.span_bug(a.span, ~"check_constraints:\
2646+
carg_ident index out of bounds");
2647+
}
2648+
}
2649+
})]
2650+
}
2651+
let p_op: ast::expr_ = ast::expr_path(c.node.path);
2652+
let oper: @ast::expr = @{id:c.node.id,
2653+
node: p_op, span:c.span};
2654+
// Another ephemeral expr
2655+
let call_expr_id = fcx.ccx.tcx.sess.next_node_id();
2656+
let call_expr = @{id: call_expr_id,
2657+
node: ast::expr_call(oper, c_args),
2658+
span: c.span};
2659+
check_pred_expr(fcx, call_expr);
2660+
}
2661+
}
2662+
26072663
fn check_fn(ccx: &@crate_ctxt, f: &ast::_fn, id: &ast::node_id,
26082664
old_fcx: &option::t<@fn_ctxt>) {
26092665
let decl = f.decl;
@@ -2620,6 +2676,7 @@ fn check_fn(ccx: &@crate_ctxt, f: &ast::_fn, id: &ast::node_id,
26202676
next_var_id: gather_result.next_var_id,
26212677
mutable fixups: fixups,
26222678
ccx: ccx};
2679+
check_constraints(fcx, decl.constraints, decl.inputs);
26232680
check_block(fcx, body);
26242681

26252682
// For non-iterator fns, we unify the tail expr's type with the

0 commit comments

Comments
 (0)