Skip to content

Commit c8739cb

Browse files
committed
Error checking for protocols. We'll need spans though.
1 parent 156eceb commit c8739cb

File tree

7 files changed

+274
-141
lines changed

7 files changed

+274
-141
lines changed

src/libcore/tuple.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,29 @@ impl extensions <T:copy, U:copy> for (T, U) {
2323

2424
}
2525

26+
impl extensions<A: copy, B: copy> for (&[A], &[B]) {
27+
fn zip() -> ~[(A, B)] {
28+
let (a, b) = self;
29+
vec::zip(a, b)
30+
}
31+
32+
fn map<C>(f: fn(A, B) -> C) -> ~[C] {
33+
let (a, b) = self;
34+
vec::map2(a, b, f)
35+
}
36+
}
37+
38+
impl extensions<A: copy, B: copy> for (~[A], ~[B]) {
39+
fn zip() -> ~[(A, B)] {
40+
let (a, b) = self;
41+
vec::zip(a, b)
42+
}
43+
44+
fn map<C>(f: fn(A, B) -> C) -> ~[C] {
45+
let (a, b) = self;
46+
vec::map2(a, b, f)
47+
}
48+
}
2649

2750
#[test]
2851
fn test_tuple() {

src/libsyntax/ext/base.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ iface ext_ctxt {
100100
fn bt_pop();
101101
fn span_fatal(sp: span, msg: ~str) -> !;
102102
fn span_err(sp: span, msg: ~str);
103+
fn span_warn(sp: span, msg: ~str);
103104
fn span_unimpl(sp: span, msg: ~str) -> !;
104105
fn span_bug(sp: span, msg: ~str) -> !;
105106
fn bug(msg: ~str) -> !;
@@ -148,6 +149,10 @@ fn mk_ctxt(parse_sess: parse::parse_sess,
148149
self.print_backtrace();
149150
self.parse_sess.span_diagnostic.span_err(sp, msg);
150151
}
152+
fn span_warn(sp: span, msg: ~str) {
153+
self.print_backtrace();
154+
self.parse_sess.span_diagnostic.span_warn(sp, msg);
155+
}
151156
fn span_unimpl(sp: span, msg: ~str) -> ! {
152157
self.print_backtrace();
153158
self.parse_sess.span_diagnostic.span_unimpl(sp, msg);

src/libsyntax/ext/pipes.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import parse::common::parser_common;
88

99
import pipes::parse_proto::proto_parser;
1010

11-
import pipes::pipec::methods;
11+
import pipes::pipec::compile;
12+
import pipes::proto::{visit, protocol};
13+
import pipes::check::proto_check;
1214

1315
fn expand_proto(cx: ext_ctxt, _sp: span, id: ast::ident,
1416
tt: ~[ast::token_tree]) -> base::mac_result
@@ -22,5 +24,9 @@ fn expand_proto(cx: ext_ctxt, _sp: span, id: ast::ident,
2224

2325
let proto = rust_parser.parse_proto(id);
2426

27+
// check for errors
28+
visit(proto, cx);
29+
30+
// compile
2531
base::mr_item(proto.compile(cx))
2632
}

src/libsyntax/ext/pipes/check.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/// Correctness for protocols
2+
3+
/*
4+
5+
This section of code makes sure the protocol is likely to generate
6+
correct code. The correctness criteria include:
7+
8+
* No protocols transition to states that don't exist.
9+
* Messages step to states with the right number of type parameters.
10+
11+
In addition, this serves as a lint pass. Lint warns for the following
12+
things.
13+
14+
* States with no messages, it's better to step to !.
15+
16+
It would also be nice to warn about unreachable states, but the
17+
visitor infrastructure for protocols doesn't currently work well for
18+
that.
19+
20+
*/
21+
22+
import dvec::extensions;
23+
24+
import ext::base::ext_ctxt;
25+
26+
import ast::{ident};
27+
28+
import proto::{state, protocol, next_state, methods};
29+
import ast_builder::empty_span;
30+
31+
impl proto_check of proto::visitor<(), (), ()> for ext_ctxt {
32+
fn visit_proto(_proto: protocol,
33+
_states: &[()]) { }
34+
35+
fn visit_state(state: state, _m: &[()]) {
36+
if state.messages.len() == 0 {
37+
self.span_warn(
38+
empty_span(), // use a real span!
39+
#fmt("state %s contains no messages, \
40+
consider stepping to a terminal state instead",
41+
*state.name))
42+
}
43+
}
44+
45+
fn visit_message(name: ident, _tys: &[@ast::ty],
46+
this: state, next: next_state) {
47+
alt next {
48+
some({state: next, tys: next_tys}) {
49+
let proto = this.proto;
50+
if !proto.has_state(next) {
51+
// This should be a span fatal, but then we need to
52+
// track span information.
53+
self.span_err(
54+
empty_span(),
55+
#fmt("message %s steps to undefined state, %s",
56+
*name, *next));
57+
}
58+
59+
let next = proto.get_state(next);
60+
61+
if next.ty_params.len() != next_tys.len() {
62+
self.span_err(
63+
empty_span(), // use a real span
64+
#fmt("message %s target (%s) \
65+
needs %u type parameters, but got %u",
66+
*name, *next.name,
67+
next.ty_params.len(),
68+
next_tys.len()));
69+
}
70+
}
71+
none { }
72+
}
73+
}
74+
}

src/libsyntax/ext/pipes/pipec.rs

Lines changed: 13 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import to_str::to_str;
55
import dvec::dvec;
66
import dvec::extensions;
77

8+
import tuple::extensions;
9+
810
import ast::ident;
911
import util::interner;
1012
import interner::{intern, get};
@@ -14,57 +16,13 @@ import ext::base::{mk_ctxt, ext_ctxt};
1416
import parse;
1517
import parse::*;
1618

19+
import proto::*;
20+
1721
import ast_builder::ast_builder;
1822
import ast_builder::methods;
1923
import ast_builder::path;
2024

21-
enum direction {
22-
send, recv
23-
}
24-
25-
impl of to_str for direction {
26-
fn to_str() -> ~str {
27-
alt self {
28-
send { ~"send" }
29-
recv { ~"recv" }
30-
}
31-
}
32-
}
33-
34-
impl methods for direction {
35-
fn reverse() -> direction {
36-
alt self {
37-
send { recv }
38-
recv { send }
39-
}
40-
}
41-
}
42-
43-
type next_state = option<{state: ident, tys: ~[@ast::ty]}>;
44-
45-
enum message {
46-
// name, data, current state, next state
47-
message(ident, ~[@ast::ty], state, next_state)
48-
}
49-
50-
impl methods for message {
51-
fn name() -> ident {
52-
alt self {
53-
message(id, _, _, _) {
54-
id
55-
}
56-
}
57-
}
58-
59-
// Return the type parameters actually used by this message
60-
fn get_params() -> ~[ast::ty_param] {
61-
alt self {
62-
message(_, _, this, _) {
63-
this.ty_params
64-
}
65-
}
66-
}
67-
25+
impl compile for message {
6826
fn gen_send(cx: ext_ctxt) -> @ast::item {
6927
#debug("pipec: gen_send");
7028
alt self {
@@ -154,34 +112,7 @@ impl methods for message {
154112
}
155113
}
156114

157-
enum state {
158-
state_(@{
159-
name: ident,
160-
dir: direction,
161-
ty_params: ~[ast::ty_param],
162-
messages: dvec<message>,
163-
proto: protocol,
164-
}),
165-
}
166-
167-
impl methods for state {
168-
fn add_message(name: ident, +data: ~[@ast::ty], next: next_state) {
169-
self.messages.push(message(name, data, self,
170-
next));
171-
}
172-
173-
fn filename() -> ~str {
174-
(*self).proto.filename()
175-
}
176-
177-
fn data_name() -> ident {
178-
self.name
179-
}
180-
181-
fn to_ty(cx: ext_ctxt) -> @ast::ty {
182-
cx.ty_path(path(self.name).add_tys(cx.ty_vars(self.ty_params)))
183-
}
184-
115+
impl compile for state {
185116
fn to_type_decls(cx: ext_ctxt) -> ~[@ast::item] {
186117
#debug("pipec: to_type_decls");
187118
// This compiles into two different type declarations. Say the
@@ -248,47 +179,7 @@ impl methods for state {
248179
}
249180
}
250181

251-
enum protocol {
252-
protocol_(@{
253-
name: ident,
254-
states: dvec<state>,
255-
}),
256-
}
257-
258-
fn protocol(name: ident) -> protocol {
259-
protocol_(@{name: name, states: dvec()})
260-
}
261-
262-
impl methods for protocol {
263-
fn add_state(name: ident, dir: direction) -> state {
264-
self.add_state_poly(name, dir, ~[])
265-
}
266-
267-
/// Get or create a state.
268-
fn get_state(name: ident) -> state {
269-
self.states.find(|i| i.name == name).get()
270-
}
271-
272-
fn add_state_poly(name: ident, dir: direction,
273-
+ty_params: ~[ast::ty_param]) -> state {
274-
let messages = dvec();
275-
276-
let state = state_(@{
277-
name: name,
278-
dir: dir,
279-
ty_params: ty_params,
280-
messages: messages,
281-
proto: self
282-
});
283-
284-
self.states.push(state);
285-
state
286-
}
287-
288-
fn filename() -> ~str {
289-
~"proto://" + *self.name
290-
}
291-
182+
impl compile for protocol {
292183
fn gen_init(cx: ext_ctxt) -> @ast::item {
293184
let start_state = self.states[0];
294185

@@ -302,18 +193,12 @@ impl methods for protocol {
302193
}
303194
};
304195

305-
parse_item_from_source_str(
306-
self.filename(),
307-
@#fmt("fn init%s() -> (client::%s, server::%s)\
308-
{ %s }",
309-
start_state.ty_params.to_source(),
310-
start_state.to_ty(cx).to_source(),
311-
start_state.to_ty(cx).to_source(),
312-
body.to_source()),
313-
cx.cfg(),
314-
~[],
315-
ast::public,
316-
cx.parse_sess()).get()
196+
cx.parse_item(#fmt("fn init%s() -> (client::%s, server::%s)\
197+
{ %s }",
198+
start_state.ty_params.to_source(),
199+
start_state.to_ty(cx).to_source(),
200+
start_state.to_ty(cx).to_source(),
201+
body.to_source()))
317202
}
318203

319204
fn compile(cx: ext_ctxt) -> @ast::item {
@@ -407,15 +292,3 @@ impl parse_utils for ext_ctxt {
407292
self.parse_sess())
408293
}
409294
}
410-
411-
impl methods<A: copy, B: copy> for (~[A], ~[B]) {
412-
fn zip() -> ~[(A, B)] {
413-
let (a, b) = self;
414-
vec::zip(a, b)
415-
}
416-
417-
fn map<C>(f: fn(A, B) -> C) -> ~[C] {
418-
let (a, b) = self;
419-
vec::map2(a, b, f)
420-
}
421-
}

0 commit comments

Comments
 (0)