From d9d091caa2d32561b664dbcbe28bda949032dced Mon Sep 17 00:00:00 2001
From: "benjamin.franksen" <benjamin.franksen@helmholtz-berlin.de>
Date: Tue, 8 May 2012 10:10:05 +0000
Subject: [PATCH] snc: issue warning if a state is not reachable from the first

---
 src/snc/analysis.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++
 src/snc/types.h    |  1 +
 2 files changed, 69 insertions(+)

diff --git a/src/snc/analysis.c b/src/snc/analysis.c
index c0f57352..6f54258e 100644
--- a/src/snc/analysis.c
+++ b/src/snc/analysis.c
@@ -40,6 +40,7 @@ static SyncQ *new_sync_queue(SyncQList *syncq_list, uint size);
 static void connect_variables(SymTable st, Expr *scope);
 static void connect_state_change_stmts(SymTable st, Expr *scope);
 static uint connect_states(SymTable st, Expr *ss_list);
+static void check_states_reachable_from_first(Expr *ss);
 static void add_var(SymTable st, Var *vp, Expr *scope);
 static Var *find_var(SymTable st, char *name, Expr *scope);
 static uint assign_ef_bits(Expr *scope);
@@ -47,6 +48,7 @@ static uint assign_ef_bits(Expr *scope);
 Program *analyse_program(Expr *prog, Options options)
 {
 	Program *p = new(Program);
+	Expr *ss;
 
 	assert(prog);	/* precondition */
 #ifdef DEBUG
@@ -78,6 +80,8 @@ Program *analyse_program(Expr *prog, Options options)
 	p->num_ss = connect_states(p->sym_table, prog);
 	connect_variables(p->sym_table, prog);
 	connect_state_change_stmts(p->sym_table, prog);
+	foreach(ss, prog->prog_statesets)
+		check_states_reachable_from_first(ss);
 	p->num_event_flags = assign_ef_bits(p->prog);
 	return p;
 }
@@ -1352,6 +1356,70 @@ static void connect_state_change_stmts(SymTable st, Expr *scope)
 		expr_mask, 0, iter_connect_state_change_stmts, &csc_arg);
 }
 
+static void mark_states_reachable_from(Expr *sp);
+
+static int iter_mark_states_reachable(Expr *ep, Expr *scope, void *parg)
+{
+	Expr *target_state = 0;
+
+	assert(ep->type == S_CHANGE || ep->type == D_WHEN);
+	switch (ep->type ) {
+	case S_CHANGE:
+		target_state = ep->extra.e_change;
+		break;
+	case D_WHEN:
+		target_state = ep->extra.e_when->next_state;
+		break;
+	}
+	if (target_state && !target_state->extra.e_state->is_target)
+	{
+		target_state->extra.e_state->is_target = 1;
+		mark_states_reachable_from(target_state);
+	}
+	return (ep->type == D_WHEN);
+}
+
+static void mark_states_reachable_from(Expr *sp)
+{
+	assert(sp);
+	assert(sp->type == D_STATE);
+
+	traverse_expr_tree(
+		sp,				/* start expression */
+		(1<<S_CHANGE)|(1<<D_WHEN),	/* when to call iteratee */
+		expr_mask,			/* when to stop descending */
+		sp,				/* current scope, 0 at top-level */
+		iter_mark_states_reachable,	/* function to call */
+		0				/* argument to pass to function */
+	);
+}
+
+static void check_states_reachable_from_first(Expr *ssp)
+{
+	Expr *sp;
+
+	assert(ssp);
+	assert(ssp->type == D_SS);
+
+	sp = ssp->ss_states;
+	assert(sp);
+	assert(sp->type == D_STATE);
+	assert(sp->extra.e_state->index == 0);
+
+	sp->extra.e_state->is_target = 1;
+	mark_states_reachable_from(sp);
+
+	foreach (sp, ssp->ss_states)
+	{
+		if (!sp->extra.e_state->is_target)
+		{
+			warning_at_expr(sp, "state '%s' in state set '%s' cannot "
+				"be reached from start state\n",
+				sp->value, ssp->value);
+		}
+	}
+}
+
 /* Assign event bits to event flags and associate pv channels with
  * event flags. Return number of event flags found.
  */
diff --git a/src/snc/types.h b/src/snc/types.h
index 0b63a1ab..3ccdb585 100644
--- a/src/snc/types.h
+++ b/src/snc/types.h
@@ -95,6 +95,7 @@ struct when				/* extra data for when clauses */
 struct state				/* extra data for state clauses */
 {
 	int		index;		/* index in array of seqState structs */
+	uint		is_target;	/* is this state a target state? */
 	StateOptions	options;	/* state options */
 	VarList		*var_list;	/* list of 'local' variables */
 };
-- 
GitLab