Index: src/AST/AssertAcyclic.cpp
===================================================================
--- src/AST/AssertAcyclic.cpp	(revision 9ef96449b6fa707000709413547abf0df2ec8683)
+++ 	(revision )
@@ -1,53 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// AssertAcyclic.cpp -- Check that ast::ptr does not form a cycle.
-//
-// Author           : Andrew Beach
-// Created On       : Thu Jun 06 15:00:00 2019
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Jun 07 14:32:00 2019
-// Update Count     : 1
-//
-
-#include "AssertAcyclic.hpp"
-
-#include "AST/Pass.hpp"
-
-namespace {
-
-class NoStrongCyclesCore {
-    std::vector<const ast::Node *> parents;
-public:
-	void previsit( const ast::Node * node ) {
-		for (auto & parent : parents) {
-			assert(parent != node);
-		}
-		parents.push_back(node);
-	}
-	void postvisit( const ast::Node * ) {
-		parents.pop_back();
-	}
-};
-
-}
-
-namespace ast {
-
-void assertAcyclic( const std::list< ast::ptr< ast::Decl > > & translationUnit ) {
-   	Pass<NoStrongCyclesCore> visitor;
-	for ( auto & decl : translationUnit ) {
-		decl->accept( visitor );
-	}
-}
-
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/AST/AssertAcyclic.hpp
===================================================================
--- src/AST/AssertAcyclic.hpp	(revision 9ef96449b6fa707000709413547abf0df2ec8683)
+++ 	(revision )
@@ -1,35 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// AssertAcyclic.hpp -- Check that ast::ptr does not form a cycle.
-//
-// Author           : Andrew Beach
-// Created On       : Thr Jun  6 15:00:00 2019
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Jun  7 14:32:00 2019
-// Update Count     : 1
-//
-
-#pragma once
-
-#include <list>
-
-#include "Node.hpp"
-namespace ast {
-    class Decl;
-};
-
-namespace ast {
-
-void assertAcyclic( const std::list< ast::ptr< ast::Decl > > & translationUnit );
-
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/AST/Util.cpp
===================================================================
--- src/AST/Util.cpp	(revision f69fac7faef547c274fcfbc767d8c423a7f3a207)
+++ src/AST/Util.cpp	(revision f69fac7faef547c274fcfbc767d8c423a7f3a207)
@@ -0,0 +1,68 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Util.hpp -- General utilities for working with the AST.
+//
+// Author           : Andrew Beach
+// Created On       : Wed Jan 19  9:46:00 2022
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Feb 18  9:42:00 2022
+// Update Count     : 0
+//
+
+#include "Util.hpp"
+
+#include "Decl.hpp"
+#include "Node.hpp"
+#include "Pass.hpp"
+#include "TranslationUnit.hpp"
+#include "Common/ScopedMap.h"
+
+#include <vector>
+
+namespace ast {
+
+namespace {
+
+/// Check that ast::ptr/strong references do not form a cycle.
+struct NoStrongCyclesCore {
+	std::vector<const Node *> parents;
+
+	void previsit( const Node * node ) {
+		for ( auto & parent : parents ) {
+			assert( parent != node );
+		}
+		parents.push_back( node );
+	}
+
+	void postvisit( const Node * node ) {
+		assert( !parents.empty() );
+		assert( parents.back() == node );
+		parents.pop_back();
+	}
+};
+
+struct InvariantCore {
+	// To save on the number of visits: this is a kind of composed core.
+	// None of the passes should make changes so ordering doesn't matter.
+	NoStrongCyclesCore no_strong_cycles;
+
+	void previsit( const Node * node ) {
+		no_strong_cycles.previsit( node );
+	}
+
+	void postvisit( const Node * node ) {
+		no_strong_cycles.postvisit( node );
+	}
+};
+
+} // namespace
+
+void checkInvariants( TranslationUnit & transUnit ) {
+	ast::Pass<InvariantCore>::run( transUnit );
+}
+
+} // namespace ast
Index: src/AST/Util.hpp
===================================================================
--- src/AST/Util.hpp	(revision f69fac7faef547c274fcfbc767d8c423a7f3a207)
+++ src/AST/Util.hpp	(revision f69fac7faef547c274fcfbc767d8c423a7f3a207)
@@ -0,0 +1,26 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Util.hpp -- General utilities for working with the AST.
+//
+// Author           : Andrew Beach
+// Created On       : Wed Jan 19  9:37:00 2022
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Feb 18  9:43:00 2022
+// Update Count     : 0
+//
+
+#pragma once
+
+namespace ast {
+
+class TranslationUnit;
+
+/// Check anything that should always be true of the AST between passes.
+/// Insert this whenever you want additional debugging checks.
+void checkInvariants( TranslationUnit & transUnit );
+
+}
Index: src/AST/module.mk
===================================================================
--- src/AST/module.mk	(revision 9ef96449b6fa707000709413547abf0df2ec8683)
+++ src/AST/module.mk	(revision f69fac7faef547c274fcfbc767d8c423a7f3a207)
@@ -16,6 +16,4 @@
 
 SRC_AST = \
-	AST/AssertAcyclic.cpp \
-	AST/AssertAcyclic.hpp \
 	AST/Attribute.cpp \
 	AST/Attribute.hpp \
@@ -64,4 +62,6 @@
 	AST/TypeSubstitution.cpp \
 	AST/TypeSubstitution.hpp \
+	AST/Util.cpp \
+	AST/Util.hpp \
 	AST/Visitor.hpp
 
