Index: libcfa/src/concurrency/clib/cfathread.cfa
===================================================================
--- libcfa/src/concurrency/clib/cfathread.cfa	(revision 5024df418d882be3d84bb5d7639c0abc9ffb8b98)
+++ libcfa/src/concurrency/clib/cfathread.cfa	(revision c715e5ff6f93573553c28ad82808e7e566136c47)
@@ -237,5 +237,5 @@
 
 typedef ThreadCancelled(cfathread_object) cfathread_exception;
-typedef ThreadCancelled_vtable(cfathread_object) cfathread_vtable;
+typedef vtable(ThreadCancelled(cfathread_object)) cfathread_vtable;
 
 void defaultResumptionHandler(ThreadCancelled(cfathread_object) & except) {
@@ -283,5 +283,5 @@
 
 typedef ThreadCancelled(__cfainit) __cfainit_exception;
-typedef ThreadCancelled_vtable(__cfainit) __cfainit_vtable;
+typedef vtable(ThreadCancelled(__cfainit)) __cfainit_vtable;
 
 void defaultResumptionHandler(ThreadCancelled(__cfainit) & except) {
Index: libcfa/src/concurrency/coroutine.hfa
===================================================================
--- libcfa/src/concurrency/coroutine.hfa	(revision 5024df418d882be3d84bb5d7639c0abc9ffb8b98)
+++ libcfa/src/concurrency/coroutine.hfa	(revision c715e5ff6f93573553c28ad82808e7e566136c47)
@@ -22,8 +22,9 @@
 //-----------------------------------------------------------------------------
 // Exception thrown from resume when a coroutine stack is cancelled.
-EHM_FORALL_EXCEPTION(CoroutineCancelled, (coroutine_t &), (coroutine_t)) (
+forall(coroutine_t &)
+exception CoroutineCancelled {
 	coroutine_t * the_coroutine;
 	exception_t * the_exception;
-);
+};
 
 forall(T &)
Index: libcfa/src/concurrency/thread.hfa
===================================================================
--- libcfa/src/concurrency/thread.hfa	(revision 5024df418d882be3d84bb5d7639c0abc9ffb8b98)
+++ libcfa/src/concurrency/thread.hfa	(revision c715e5ff6f93573553c28ad82808e7e566136c47)
@@ -32,8 +32,9 @@
 };
 
-EHM_FORALL_EXCEPTION(ThreadCancelled, (thread_t &), (thread_t)) (
+forall(thread_t &)
+exception ThreadCancelled {
 	thread_t * the_thread;
 	exception_t * the_exception;
-);
+};
 
 forall(T &)
Index: libcfa/src/exception.hfa
===================================================================
--- libcfa/src/exception.hfa	(revision 5024df418d882be3d84bb5d7639c0abc9ffb8b98)
+++ libcfa/src/exception.hfa	(revision c715e5ff6f93573553c28ad82808e7e566136c47)
@@ -10,6 +10,6 @@
 // Created On       : Thu Apr  7 10:25:00 2020
 // Last Modified By : Andrew Beach
-// Last Modified On : Thr Apr  8 15:16:00 2021
-// Update Count     : 4
+// Last Modified On : Wed May 25 17:20:00 2022
+// Update Count     : 5
 //
 
@@ -18,54 +18,8 @@
 // -----------------------------------------------------------------------------------------------
 
-// EHM_EXCEPTION(exception_name)(fields...);
-// Create an exception (a virtual structure that inherits from exception_t)
-// with the given name and fields.
-#define EHM_EXCEPTION(exception_name) \
-	_EHM_TYPE_ID_STRUCT(exception_name, ); \
-	_EHM_TYPE_ID_VALUE(exception_name, ); \
-	_EHM_VIRTUAL_TABLE_STRUCT(exception_name, , ); \
-	_EHM_EXCEPTION_STRUCT(exception_name, , )
-
-// EHM_EXTERN_VTABLE(exception_name, table_name);
-// Forward declare a virtual table called table_name for exception_name type.
-#define EHM_EXTERN_VTABLE(exception_name, table_name) \
-	_EHM_EXTERN_VTABLE(exception_name, , table_name)
-
-// EHM_VIRTUAL_TABLE(exception_name, table_name);
-// Define a virtual table called table_name for exception_name type.
-#define EHM_VIRTUAL_TABLE(exception_name, table_name) \
-	_EHM_DEFINE_COPY(exception_name, ) \
-	_EHM_DEFINE_MSG(exception_name, ) \
-	_EHM_VIRTUAL_TABLE(exception_name, , table_name)
-
-// EHM_FORALL_EXCEPTION(exception_name, (assertions), (parameters))(fields...);
-// As EHM_EXCEPTION but for polymorphic types instead of monomorphic ones.
-// The assertions list should include all polymorphic parameters and
-// assertions inside a parentisized list. Parameters should include all the
-// polymorphic parameter names inside a parentisized list (same order).
-#define EHM_FORALL_EXCEPTION(exception_name, assertions, parameters) \
-	_EHM_TYPE_ID_STRUCT(exception_name, forall assertions); \
-	_EHM_VIRTUAL_TABLE_STRUCT(exception_name, forall assertions, parameters); \
-	_EHM_EXCEPTION_STRUCT(exception_name, forall assertions, parameters)
-
-// EHM_FORALL_EXTERN_VTABLE(exception_name, (arguments), table_name);
-// As EHM_EXTERN_VTABLE but for polymorphic types instead of monomorphic ones.
-// Arguments should be the parentisized list of polymorphic arguments.
-#define EHM_FORALL_EXTERN_VTABLE(exception_name, arguments, table_name) \
-	_EHM_EXTERN_VTABLE(exception_name, arguments, table_name)
-
-// EHM_FORALL_VIRTUAL_TABLE(exception_name, (arguments), table_name);
-// As EHM_VIRTUAL_TABLE but for polymorphic types instead of monomorphic ones.
-// Arguments should be the parentisized list of polymorphic arguments.
-#define EHM_FORALL_VIRTUAL_TABLE(exception_name, arguments, table_name) \
-	_EHM_TYPE_ID_VALUE(exception_name, arguments); \
-	_EHM_DEFINE_COPY(exception_name, arguments) \
-	_EHM_DEFINE_MSG(exception_name, arguments) \
-	_EHM_VIRTUAL_TABLE(exception_name, arguments, table_name)
-
 // EHM_DEFAULT_VTABLE(exception_name, (arguments))
 // Create a declaration for a (possibly polymorphic) default vtable.
 #define EHM_DEFAULT_VTABLE(exception_name, arguments) \
-	_EHM_VTABLE_TYPE(exception_name) arguments & const _default_vtable
+	vtable(exception_name arguments) & const _default_vtable
 
 // IS_EXCEPTION(exception_name [, (...parameters)])
@@ -77,110 +31,4 @@
 #define IS_RESUMPTION_EXCEPTION(...) _IS_EXCEPTION(is_resumption_exception, __VA_ARGS__, , ~)
 #define IS_TERMINATION_EXCEPTION(...) _IS_EXCEPTION(is_termination_exception, __VA_ARGS__, , ~)
-
-// Macros starting with a leading underscore are internal.
-
-// Create an exception type definition. must be tailing, can be polymorphic.
-#define _EHM_EXCEPTION_STRUCT(exception_name, forall_clause, parameters) \
-	forall_clause struct exception_name { \
-		_EHM_VTABLE_TYPE(exception_name) parameters const * virtual_table; \
-		_CLOSE
-
-// Create a (possibly polymorphic) virtual table forward declaration.
-#define _EHM_EXTERN_VTABLE(exception_name, arguments, table_name) \
-	extern const _EHM_VTABLE_TYPE(exception_name) arguments table_name
-
-// Create a (possibly polymorphic) virtual table definition.
-#define _EHM_VIRTUAL_TABLE(exception_type, arguments, table_name) \
-	const _EHM_VTABLE_TYPE(exception_type) arguments table_name @= { \
-		.__cfavir_typeid : &_EHM_TYPE_ID_NAME(exception_type), \
-		.size : sizeof(struct exception_type arguments), \
-		.copy : copy, \
-		.^?{} : ^?{}, \
-		.msg : msg, \
-	}
-
-// Create a (possibly polymorphic) copy function from an assignment operator.
-#define _EHM_DEFINE_FORALL_COPY(exception_name, forall_clause, parameters) \
-	forall_clause void copy(exception_name parameters * this, \
-			exception_name parameters * that) { \
-		*this = *that; \
-	}
-
-#define _EHM_DEFINE_COPY(exception_name, arguments) \
-	void copy(exception_name arguments * this, exception_name arguments * that) { \
-		*this = *that; \
-	}
-
-// Create a (possibly polymorphic) msg function
-#define _EHM_DEFINE_FORALL_MSG(exception_name, forall_clause, parameters) \
-	forall_clause const char * msg(exception_name parameters * this) { \
-		return #exception_name #parameters; \
-	}
-
-#define _EHM_DEFINE_MSG(exception_name, arguments) \
-	const char * msg(exception_name arguments * this) { \
-		return #exception_name #arguments; \
-	}
-
-// Produces the C compatable name of the virtual table type for a virtual type.
-#define _EHM_VTABLE_TYPE(type_name) struct _GLUE2(type_name,_vtable)
-
-// Create the vtable type for exception name.
-#define _EHM_VIRTUAL_TABLE_STRUCT(exception_name, forall_clause, parameters) \
-	forall_clause struct exception_name; \
-	forall_clause _EHM_VTABLE_TYPE(exception_name) { \
-		_EHM_TYPE_ID_TYPE(exception_name) parameters const * __cfavir_typeid; \
-		size_t size; \
-		void (*copy)(exception_name parameters * this, exception_name parameters * other); \
-		void (*^?{})(exception_name parameters & this); \
-		const char * (*msg)(exception_name parameters * this); \
-	}
-
-// Define the function required to satify the trait for exceptions.
-#define _EHM_TRAIT_FUNCTION(exception_name, forall_clause, parameters) \
-	forall_clause inline void mark_exception( \
-		exception_name parameters const &, \
-		_EHM_VTABLE_TYPE(exception_name) parameters const &) {} \
-
-#define __EHM_TRAIT_FUNCTION(exception_name, forall_clause, parameters) \
-	forall_clause inline _EHM_VTABLE_TYPE(exception_name) parameters const & \
-			get_exception_vtable(exception_name parameters const & this) { \
-		/* This comes before the structure definition, but we know the offset. */ \
-		/* return (_EHM_VTABLE_TYPE(exception_name) parameters const &)this; */ \
-		assert(false); \
-	}
-
-// Generates a new type-id structure. This is used to mangle the name of the
-// type-id instance so it also includes polymorphic information. Must be the
-// direct decendent of exception_t.
-// The second field is used to recover type information about the exception.
-#define _EHM_TYPE_ID_STRUCT(exception_name, forall_clause) \
-	forall_clause _EHM_TYPE_ID_TYPE(exception_name) { \
-		__cfavir_type_info const * parent; \
-	}
-
-// Generate a new type-id value.
-#define _EHM_TYPE_ID_VALUE(exception_name, arguments) \
-	__attribute__((cfa_linkonce)) \
-	_EHM_TYPE_ID_TYPE(exception_name) arguments const \
-			_EHM_TYPE_ID_NAME(exception_name) = { \
-		&__cfatid_exception_t, \
-	}
-
-// _EHM_TYPE_ID_STRUCT and _EHM_TYPE_ID_VALUE are the two that would need to
-// be updated to extend the hierarchy if we are still using macros when that
-// is added.
-
-// Produce the C compatable name of the type-id type for an exception type.
-#define _EHM_TYPE_ID_TYPE(exception_name) \
-	struct _GLUE2(__cfatid_struct_, exception_name)
-
-// Produce the name of the instance of the type-id for an exception type.
-#define _EHM_TYPE_ID_NAME(exception_name) _GLUE2(__cfatid_,exception_name)
-
 #define _IS_EXCEPTION(kind, exception_name, parameters, ...) \
-	kind(exception_name parameters, _EHM_VTABLE_TYPE(exception_name) parameters)
-
-// Internal helper macros:
-#define _CLOSE(...) __VA_ARGS__ }
-#define _GLUE2(left, right) left##right
+	kind(exception_name parameters, vtable(exception_name parameters))
Index: src/CodeGen/GenType.cc
===================================================================
--- src/CodeGen/GenType.cc	(revision 5024df418d882be3d84bb5d7639c0abc9ffb8b98)
+++ src/CodeGen/GenType.cc	(revision c715e5ff6f93573553c28ad82808e7e566136c47)
@@ -10,6 +10,6 @@
 // Created On       : Mon May 18 07:44:20 2015
 // Last Modified By : Andrew Beach
-// Last Modified On : Wed May  1 15:24:00 2019
-// Update Count     : 23
+// Last Modified On : Fri May 20 11:18:00 2022
+// Update Count     : 24
 //
 #include "GenType.h"
@@ -50,4 +50,5 @@
 		void postvisit( TraitInstType * inst );
 		void postvisit( TypeofType * typeof );
+		void postvisit( VTableType * vtable );
 		void postvisit( QualifiedType * qualType );
 
@@ -259,10 +260,11 @@
 			if ( options.genC ) {
 				typeString = "enum " + typeString;
-			} 
-		} 
+			}
+		}
 		handleQualifiers( enumInst );
 	}
 
 	void GenType::postvisit( TypeInstType * typeInst ) {
+		assertf( ! options.genC, "Type instance types should not reach code generation." );
 		typeString = typeInst->name + " " + typeString;
 		handleQualifiers( typeInst );
@@ -320,4 +322,12 @@
 	}
 
+	void GenType::postvisit( VTableType * vtable ) {
+		assertf( ! options.genC, "Virtual table types should not reach code generation." );
+		std::ostringstream os;
+		os << "vtable(" << genType( vtable->base, "", options ) << ") " << typeString;
+		typeString = os.str();
+		handleQualifiers( vtable );
+	}
+
 	void GenType::postvisit( QualifiedType * qualType ) {
 		assertf( ! options.genC, "Qualified types should not reach code generation." );
Index: src/ControlStruct/ExceptDecl.cc
===================================================================
--- src/ControlStruct/ExceptDecl.cc	(revision 5024df418d882be3d84bb5d7639c0abc9ffb8b98)
+++ src/ControlStruct/ExceptDecl.cc	(revision c715e5ff6f93573553c28ad82808e7e566136c47)
@@ -9,7 +9,7 @@
 // Author           : Henry Xue
 // Created On       : Tue Jul 20 04:10:50 2021
-// Last Modified By : Henry Xue
-// Last Modified On : Tue Aug 03 10:42:26 2021
-// Update Count     : 4
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed May 25 16:43:00 2022
+// Update Count     : 5
 //
 
@@ -39,12 +39,11 @@
 }
 
-TypeInstType * makeExceptInstType(
-	const std::string & exceptionName,
-	const std::list< Expression *> & parameters
-) {
-	TypeInstType * exceptInstType = new TypeInstType(
+StructInstType * makeExceptInstType(
+	const std::string & exceptionName,
+	const std::list< Expression *> & parameters
+) {
+	StructInstType * exceptInstType = new StructInstType(
 		noQualifiers,
-		exceptionName,
-		false
+		exceptionName
 	);
 	cloneAll( parameters, exceptInstType->parameters );
@@ -151,5 +150,5 @@
 		nullptr,
 		new PointerType( noQualifiers,
-			new TypeInstType( Type::Const, "__cfavir_type_info", false ) ),
+			new StructInstType( Type::Const, "__cfavir_type_info" ) ),
 		nullptr
 	) );
@@ -257,5 +256,5 @@
 	const std::string & exceptionName,
 	const std::list< TypeDecl *> & forallClause,
-	const std::list< Expression *> & parameters, 
+	const std::list< Expression *> & parameters,
 	const std::list< Declaration *> & members
 ) {
@@ -302,5 +301,5 @@
 ObjectDecl * ehmExternVtable(
 	const std::string & exceptionName,
-	const std::list< Expression *> & parameters, 
+	const std::list< Expression *> & parameters,
 	const std::string & tableName
 ) {
@@ -457,8 +456,27 @@
 }
 
+class VTableCore : public WithDeclsToAdd {
+public:
+	// Remove any remaining vtable type nodes in the tree.
+	Type * postmutate( VTableType * vtableType );
+};
+
+Type * VTableCore::postmutate( VTableType * vtableType ) {
+	auto inst = strict_dynamic_cast<ReferenceToType *>( vtableType->base );
+
+	std::string vtableName = Virtual::vtableTypeName( inst->name );
+	StructInstType * newType = new StructInstType( noQualifiers, vtableName );
+	cloneAll( inst->parameters, newType->parameters );
+
+	delete vtableType;
+	return newType;
+}
+
 void translateExcept( std::list< Declaration *> & translationUnit ) {
 	PassVisitor<ExceptDeclCore> translator;
 	mutateAll( translationUnit, translator );
-}
-
-}
+	PassVisitor<VTableCore> typeTranslator;
+	mutateAll( translationUnit, typeTranslator );
+}
+
+}
Index: tests/.expect/quasiKeyword.txt
===================================================================
--- tests/.expect/quasiKeyword.txt	(revision 5024df418d882be3d84bb5d7639c0abc9ffb8b98)
+++ tests/.expect/quasiKeyword.txt	(revision c715e5ff6f93573553c28ad82808e7e566136c47)
@@ -1,1 +1,1 @@
-quasiKeyword.cfa:54:25: warning: Compiled
+quasiKeyword.cfa:52:25: warning: Compiled
Index: tests/exceptions/defaults.cfa
===================================================================
--- tests/exceptions/defaults.cfa	(revision 5024df418d882be3d84bb5d7639c0abc9ffb8b98)
+++ tests/exceptions/defaults.cfa	(revision c715e5ff6f93573553c28ad82808e7e566136c47)
@@ -2,5 +2,4 @@
 
 #include <string.h>
-#include <exception.hfa>
 
 exception log_message {
@@ -8,9 +7,19 @@
 };
 
-_EHM_DEFINE_COPY(log_message, )
+void copy(log_message * this, log_message * that) {
+	*this = *that;
+}
+
 const char * msg(log_message * this) {
 	return this->msg;
 }
-_EHM_VIRTUAL_TABLE(log_message, , log_vt);
+
+const struct log_message_vtable log_vt @= {
+	.__cfavir_typeid : &__cfatid_log_message,
+	.size : sizeof(struct log_message),
+	.copy : copy,
+	.^?{} : ^?{},
+	.msg : msg,
+};
 
 // Logging messages don't have to be handled.
Index: tests/linking/exception-nothreads.cfa
===================================================================
--- tests/linking/exception-nothreads.cfa	(revision 5024df418d882be3d84bb5d7639c0abc9ffb8b98)
+++ tests/linking/exception-nothreads.cfa	(revision c715e5ff6f93573553c28ad82808e7e566136c47)
@@ -15,8 +15,7 @@
 
 #include <stdlib.hfa>
-#include <exception.hfa>
 
-EHM_EXCEPTION(ping)();
-EHM_VIRTUAL_TABLE(ping, ping_vt);
+exception ping {};
+vtable(ping) ping_vt;
 
 int main(void) {
Index: tests/linking/exception-withthreads.cfa
===================================================================
--- tests/linking/exception-withthreads.cfa	(revision 5024df418d882be3d84bb5d7639c0abc9ffb8b98)
+++ tests/linking/exception-withthreads.cfa	(revision c715e5ff6f93573553c28ad82808e7e566136c47)
@@ -15,9 +15,8 @@
 
 #include <stdlib.hfa>
-#include <exception.hfa>
 #include "../exceptions/with-threads.hfa"
 
-EHM_EXCEPTION(ping)();
-EHM_VIRTUAL_TABLE(ping, ping_vt);
+exception ping {};
+vtable(ping) ping_vt;
 
 int main(void) {
Index: tests/quasiKeyword.cfa
===================================================================
--- tests/quasiKeyword.cfa	(revision 5024df418d882be3d84bb5d7639c0abc9ffb8b98)
+++ tests/quasiKeyword.cfa	(revision c715e5ff6f93573553c28ad82808e7e566136c47)
@@ -4,5 +4,5 @@
 // quasiKeyword.cfa -- test that quasi-keywords can be used for variable and functions names, as well as keywords in
 //					   control structures.
-// 
+//
 // Author           : Peter A. Buhr
 // Created On       : Wed Feb 17 10:33:49 2021
@@ -10,9 +10,7 @@
 // Last Modified On : Sat Jun  5 10:07:59 2021
 // Update Count     : 8
-// 
+//
 
-#include <exception.hfa>
-
-EHM_EXCEPTION( E )();
+exception E {};
 
 void catch( int i ) {}
@@ -49,5 +47,5 @@
 		} fixup ( E * ) {
 		} finally {
-		} 
+		}
 	else catch = 3;
 
