/* Translation rules for exception handling code, from Cforall to C.
 *
 * Reminder: This is not final. Besides names and things it is also going very
 * much for correctness and simplisity over efficiency.
 *
 * The first section is the shared definitions, not generated by the local
 * translations but used by the translated code.
 *
 * Most of these exist only after translation (in C code). The first (the
 * exception type) has to exist in Cforall code so that it can be used
 * directly in Cforall. The two __throw_* functions might have wrappers in
 * Cforall, but the underlying functions should probably be C. struct
 * stack_exception_data has to exist inside of the coroutine data structures
 * and so should be compiled as they are.
 */

// Currently it is a typedef for int, but later it will be a new type.
typedef int exception;

// Will have to be availibe to user. Consider new name. Requires tagged types.
forall(dtype parent | tagged(parent), dtype child | tagged(child))
parent *dynamic_cast(child *);

void __throw_terminate(exception except) __attribute__((noreturn));
void __rethrow_terminate() __attribute__((noreturn));
void __throw_resume(exception except);

void __try_terminate(void (*try_block)(),
	void (*catch_block)(int index, exception except),
	int (*match_block)(exception except));

struct __try_resume_node {
	struct __try_resume_node * next;
	bool (*try_to_handle)(exception except);
};

void __try_resume_cleanup(struct __try_resume_node * node);

struct __cleanup_hook {};

// An instance of the following must be paired with every stack.
struct stack_exception_data {
	__try_resume_node * top_resume_data;
	// Other pointers may be required for re-resume.

	exception current_exception;
	int handler_index;
};


// Translations:

// Throws:
"Cforall"

throw exception_instance;

resume exception_instance;

"C"

__throw_terminate(exception_instance);

__throw_resume(exception_instance);



// Rethrows (inside matching handlers):
"Cforall"

throw;

resume;

"C"

__rethrow_terminate();

return false;


// Termination Handlers:
"Cforall"

void try_terminate() {
	try {
		insideTry();
	}
	catch (SomeException) {
		fiddleThing();
	}
	catch (OtherException err ; err.priority > 3) {
		twiddleWidget();
	}
}

"C"

void try_terminate() {
	{
		void try1() {
			insideTry();
		}
		// index is not nessasary, but should be much faster than going over
		// all the checks in if we can find a way to pass it in.
		void catch1(exception except, int index) {
			switch (index) {
			case 1:
				// if it is referenced in the handler, cast except.
				{
					fiddleThing();
				}
				return;
			case 2:
				{
					twiddleWidget();
				}
				return;
			default:
				// Error, should never be reached.
			}
		}
		int match1(exception except) {
			{
				if (dynamic_cast__SomeException(except)) {
					return 1;
				}
			}
			{
				OtherException err;
				if ( (err = dynamic_cast__OtherException(except)) &&
						err.priority > 3) {
					return 2;
				}
			}
			return 0;
		}
		__try_terminate(try1, catch1, match1);
	}
}


// Resumption Handlers:
"Cforall"

void try_resume() {
	try {
		insideTry();
	}
	catch resume (SomeException) {
		fiddleThing();
	}
	catch resume (OtherException err ; err.priority > 3) {
		twiddleWidget();
	}
}

"C"

void try_resume() {
	{
		bool handle1(exception except) {
			{
				if (dynamic_cast__SomeException(except)) {
					fiddleThing();
					return true;
				}
			}
			{
				OtherException err;
 				if ( ( err = dynamic_cast__OtherException(except) ) &&
						err.priority > 3) {
					twiddleWidget();
					return true;
				}
			}
			return false;
		}
		struct __try_resume_node data =
			{.next = stack.except.top_resume, .try_to_handle = handle1};
		stack.except.top_resume = &data;

		struct __cleanup_hook generated_name
			__attribute__((cleanup(__try_resume_cleanup)));

		{
			insideTry();
		}
	}
}


// Finally Clause:
"Cforall"

void try_finally() {
	try {
		insideTry();
	}
	finally {
		twiddleWidget();
	}
}

"C"

void try_finally() {
	{
		void finally1()	{
			twiddleWidget();
		}

		struct __cleanup_hook generated_name
			__attribute__((cleanup(finally1)));

		{
			insideTry();
		}
	}
}


// Resume + Finally:
"Cforall"

void try_resume_finally() {
	try {
		insideTry();
	}
	catch resume (SomeException) {
		fiddleThing();
	}
	finally {
		twiddleWidget();
	}
}

"C"

void try_resume_finally() {
	{
		void finally1() {
			twiddleWidget();
		}
		bool handle1(exception except) {
			{
				if (dynamic_cast__SomeException(except)) {
					fiddleThing();
					return true;
				}
			}
			return false;
		}
		struct __cleanup_hook generated_name
			__attribute__((cleanup(finally1)));

		struct __try_resume_node data =
			{.next = stack.except.top_resume, .try_to_handle = handle1};
		stack.except.top_resume = &data;

		struct __cleanup_hook generated_name
			__attribute__((cleanup(__try_resume_cleanup)));
	}
}


// Terminate + Resume + Finally:
"Cforall"

void try_all() {
	try {
		insideTry();
	}
	catch (SomeException) {
		fiddleThing();
	}
	catch resume (OtherException) {
		twiddleWidget();
	}
	finally {
		twiddleWidget();
	}
}

"C"

void try_all() {
	{
		bool handle1() {
			{
				if (dynamic_cast__OtherException(except)) {
					twiddleWidget();
					return true;
				}
			}
			return false;
		}
		void try1 () {
			struct __try_resume_node generated_name =
				{.next = stack.except.top_resume, .try_to_handle = handle1}
				__attribute__((cleanup(__try_resume_cleanup)));
			stack.except.top_resume = &data;

			insideTry();
		}
		void catch1(exception except, int index) {
			switch (index) {
			case 1:
				fiddleThing();
				break;
			default:
				// Error if reached.
			}
		}
		int match1(exception except) {
			{
				if (dynamic_cast__SomeException(except)) {
					return 1;
				}
			}
			return 0;
		}
		void finally1() {

			twiddleWidget();
		}
		struct __cleanup_hook generated_name
			__attribute__((cleanup(finally1)));

		__try_terminate(try1, catch1, match1);
	}
}
