Index: src/AST/Node.hpp
===================================================================
--- src/AST/Node.hpp	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
+++ src/AST/Node.hpp	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
@@ -0,0 +1,156 @@
+#pragma once
+
+#include <memory>
+
+namespace ast {
+
+	class Node {
+	public:
+		virtual ~Node() = default;
+
+		enum class ref_type {
+			strong,
+			weak
+		};
+
+		void increment(ref_type ref) {
+			switch (ref) 			{
+				case ref_type::strong: strong_ref++; break;
+				case ref_type::weak  : weak_ref  ++; break;
+			}
+		}
+
+		void decrement(ref_type ref) {
+			switch (ref) 			{
+				case ref_type::strong: strong_ref--; break;
+				case ref_type::weak  : weak_ref  --; break;
+			}
+
+			if(!strong_ref && !weak_ref) {
+				delete this;
+			}
+		}
+
+	private:
+		size_t strong_ref = 0;
+		size_t weak_ref = 0;
+	};
+
+	template< typename node_t, enum  Node::ref_type ref_t>
+	class ptr_base {
+	public:
+		ptr_base() : node(nullptr) {}
+		ptr_base( node_t * n ) : node(n) { if( !node ) node->increment(ref_t); }
+		~ptr_base() { if( node ) node->decrement(ref_t); }
+
+		template< enum  Node::ref_type o_ref_t >
+		ptr_base( const ptr_base<node_t, o_ref_t> & o ) : node(o.node) {
+			if( !node ) return;
+			node->increment(ref_t);
+		}
+
+		template< enum  Node::ref_type o_ref_t >
+		ptr_base( ptr_base<node_t, o_ref_t> && o ) : node(o.node) {
+			if( node ) node->increment(ref_t);
+			if( node ) node->decrement(o_ref_t);
+		}
+
+		template< enum  Node::ref_type o_ref_t >
+		ptr_base & operator=( const ptr_base<node_t, o_ref_t> & o ) {
+			assign(o.node);
+			return *this;
+		}
+
+		template< enum  Node::ref_type o_ref_t >
+		ptr_base & operator=( ptr_base<node_t, o_ref_t> && o ) {
+			if(o.node == node) return *this;
+			assign(o.node);
+			if( node ) node->decrement(o_ref_t);
+			return *this;
+		}
+
+		const node_t * get() const { return  node; }
+		const node_t * operator->() const { return  node; }
+		const node_t & operator* () const { return *node; }
+		operator bool() const { return node; }
+
+	private:
+		void assign(node_t * other ) {
+			if( other ) other->increment(ref_t);
+			if( node  ) node ->decrement(ref_t);
+			node = other;
+		}
+
+	protected:
+		node_t * node;
+	};
+
+	template< typename node_t >
+	class ptr : public ptr_base< node_t, Node::ref_type::strong > {
+	public:
+		typedef ptr_base< node_t, Node::ref_type::strong > base_t;
+
+		ptr() = default;
+		ptr( node_t node ) : base_t( node ) {}
+		~ptr() = default;
+
+		template< enum  Node::ref_type ref_t >
+		ptr( const ptr_base<node_t, ref_t> & o ) : base_t(o) {}
+
+		template< enum  Node::ref_type ref_t >
+		ptr( ptr_base<node_t, ref_t> && o ) : base_t( std::move(o) ) {}
+
+		template< enum  Node::ref_type o_ref_t >
+		ptr & operator=( const ptr_base<node_t, o_ref_t> & o ) {
+			base_t::operator=(o);
+			return *this;
+		}
+
+		template< enum  Node::ref_type o_ref_t >
+		ptr & operator=( ptr_base<node_t, o_ref_t> && o ) {
+			base_t::operator=(std::move(o));
+			return *this;
+		}
+
+		node_t * mutate() {
+			using base_t::node;
+			assert(node->strong_count);
+			if (node->strong_count == 1) {
+				return node;
+			}
+
+			assertf(node->weak_count == 0, "Error: mutating node with weak references to it will invalided some references");
+			auto n = new node_t(*node);
+			assign(n);
+			return node;
+		}
+	};
+
+	template< typename node_t >
+	class readonly : public ptr_base< node_t, Node::ref_type::weak > {
+	public:
+		typedef ptr_base< node_t, Node::ref_type::strong > base_t;
+
+		readonly() = default;
+		readonly( node_t node ) : base_t( node ) {}
+		~readonly() = default;
+
+		template< enum  Node::ref_type ref_t >
+		readonly( const ptr_base<node_t, ref_t> & o ) : base_t(o) {}
+
+		template< enum  Node::ref_type ref_t >
+		readonly( ptr_base<node_t, ref_t> && o ) : base_t( std::move(o) ) {}
+
+		template< enum  Node::ref_type o_ref_t >
+		readonly & operator=( const ptr_base<node_t, o_ref_t> & o ) {
+			base_t::operator=(o);
+			return *this;
+		}
+
+		template< enum  Node::ref_type o_ref_t >
+		readonly & operator=( ptr_base<node_t, o_ref_t> && o ) {
+			base_t::operator=(std::move(o));
+			return *this;
+		}
+	};
+}
Index: src/Common/PassVisitor.h
===================================================================
--- src/Common/PassVisitor.h	(revision 1c9568f0e5ea366b2f5bcb84dad6b74b891993d6)
+++ src/Common/PassVisitor.h	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
@@ -4,4 +4,5 @@
 
 #include <stack>
+#include <type_traits>
 
 #include "Common/Stats.h"
@@ -301,5 +302,5 @@
 
 
-	TypeSubstitution ** 		get_env_ptr    () { return env_impl             ( pass, 0); }
+	auto			 		get_env_ptr    () -> decltype(env_impl( pass, 0)) { return env_impl( pass, 0); }
 	std::list< Statement* > * 	get_beforeStmts() { return stmtsToAddBefore_impl( pass, 0); }
 	std::list< Statement* > * 	get_afterStmts () { return stmtsToAddAfter_impl ( pass, 0); }
@@ -348,4 +349,13 @@
 };
 
+class WithConstTypeSubstitution {
+protected:
+	WithConstTypeSubstitution() = default;
+	~WithConstTypeSubstitution() = default;
+
+public:
+	const TypeSubstitution * env = nullptr;
+};
+
 class WithStmtsToAdd {
 protected:
Index: src/Common/PassVisitor.impl.h
===================================================================
--- src/Common/PassVisitor.impl.h	(revision 1c9568f0e5ea366b2f5bcb84dad6b74b891993d6)
+++ src/Common/PassVisitor.impl.h	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
@@ -253,5 +253,5 @@
 
 	// don't want statements from outer CompoundStmts to be added to this CompoundStmt
-	ValueGuardPtr< TypeSubstitution * >  oldEnv        ( get_env_ptr    () );
+	ValueGuardPtr< typename std::remove_pointer<decltype(get_env_ptr())>::type >  oldEnv( get_env_ptr() );
 	ValueGuardPtr< DeclList_t >          oldBeforeDecls( get_beforeDecls() );
 	ValueGuardPtr< DeclList_t >          oldAfterDecls ( get_afterDecls () );
@@ -1995,5 +1995,5 @@
 
 	// don't want statements from outer CompoundStmts to be added to this StmtExpr
-	ValueGuardPtr< TypeSubstitution * >      oldEnv        ( get_env_ptr() );
+	ValueGuardPtr< typename std::remove_pointer<decltype(get_env_ptr())>::type >  oldEnv( get_env_ptr() );
 	ValueGuardPtr< std::list< Statement* > > oldBeforeStmts( get_beforeStmts() );
 	ValueGuardPtr< std::list< Statement* > > oldAfterStmts ( get_afterStmts () );
@@ -2012,5 +2012,5 @@
 
 	// don't want statements from outer CompoundStmts to be added to this StmtExpr
-	ValueGuardPtr< TypeSubstitution * >      oldEnv        ( get_env_ptr() );
+	ValueGuardPtr< typename std::remove_pointer<decltype(get_env_ptr())>::type >  oldEnv( get_env_ptr() );
 	ValueGuardPtr< std::list< Statement* > > oldBeforeStmts( get_beforeStmts() );
 	ValueGuardPtr< std::list< Statement* > > oldAfterStmts ( get_afterStmts () );
Index: src/Common/PassVisitor.proto.h
===================================================================
--- src/Common/PassVisitor.proto.h	(revision 1c9568f0e5ea366b2f5bcb84dad6b74b891993d6)
+++ src/Common/PassVisitor.proto.h	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
@@ -165,5 +165,5 @@
 static inline type * name##_impl( __attribute__((unused)) pass_type& pass, __attribute__((unused)) long unused ) { return nullptr;}    \
 
-FIELD_PTR( TypeSubstitution *, env )
+FIELD_PTR( const TypeSubstitution *, env )
 FIELD_PTR( std::list< Statement* >, stmtsToAddBefore )
 FIELD_PTR( std::list< Statement* >, stmtsToAddAfter  )
Index: src/GenPoly/Box.cc
===================================================================
--- src/GenPoly/Box.cc	(revision 1c9568f0e5ea366b2f5bcb84dad6b74b891993d6)
+++ src/GenPoly/Box.cc	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
@@ -76,5 +76,5 @@
 
 		/// Replaces polymorphic return types with out-parameters, replaces calls to polymorphic functions with adapter calls as needed, and adds appropriate type variables to the function call
-		class Pass1 final : public BoxPass, public WithTypeSubstitution, public WithStmtsToAdd, public WithGuards, public WithVisitorRef<Pass1>, public WithShortCircuiting {
+		class Pass1 final : public BoxPass, public WithConstTypeSubstitution, public WithStmtsToAdd, public WithGuards, public WithVisitorRef<Pass1>, public WithShortCircuiting {
 		  public:
 			Pass1();
@@ -150,5 +150,5 @@
 		/// * Calculates polymorphic offsetof expressions from offset array
 		/// * Inserts dynamic calculation of polymorphic type layouts where needed
-		class PolyGenericCalculator final : public BoxPass, public WithGuards, public WithVisitorRef<PolyGenericCalculator>, public WithStmtsToAdd, public WithDeclsToAdd, public WithTypeSubstitution {
+		class PolyGenericCalculator final : public BoxPass, public WithGuards, public WithVisitorRef<PolyGenericCalculator>, public WithStmtsToAdd, public WithDeclsToAdd, public WithConstTypeSubstitution {
 		public:
 			PolyGenericCalculator();
@@ -1764,7 +1764,7 @@
 
 		Expression *PolyGenericCalculator::postmutate( SizeofExpr *sizeofExpr ) {
-			Type *ty = sizeofExpr->get_isType() ? 
+			Type *ty = sizeofExpr->get_isType() ?
 				sizeofExpr->get_type() : sizeofExpr->get_expr()->get_result();
-			
+
 			Expression * gen = genSizeof( ty );
 			if ( gen ) {
Index: src/GenPoly/GenPoly.cc
===================================================================
--- src/GenPoly/GenPoly.cc	(revision 1c9568f0e5ea366b2f5bcb84dad6b74b891993d6)
+++ src/GenPoly/GenPoly.cc	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
@@ -440,5 +440,5 @@
 	}
 
-	bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, TypeSubstitution * env ) {
+	bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, const TypeSubstitution * env ) {
 		// is parameter is not polymorphic, don't need to box
 		if ( ! isPolyType( param, exprTyVars ) ) return false;
@@ -450,5 +450,5 @@
 	}
 
-	bool needsBoxing( Type * param, Type * arg, ApplicationExpr * appExpr, TypeSubstitution * env ) {
+	bool needsBoxing( Type * param, Type * arg, ApplicationExpr * appExpr, const TypeSubstitution * env ) {
 		FunctionType * function = getFunctionType( appExpr->function->result );
 		assertf( function, "ApplicationExpr has non-function type: %s", toString( appExpr->function->result ).c_str() );
Index: src/GenPoly/GenPoly.h
===================================================================
--- src/GenPoly/GenPoly.h	(revision 1c9568f0e5ea366b2f5bcb84dad6b74b891993d6)
+++ src/GenPoly/GenPoly.h	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
@@ -81,8 +81,8 @@
 
 	/// true if arg requires boxing given exprTyVars
-	bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, TypeSubstitution * env );
+	bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, const TypeSubstitution * env );
 
 	/// true if arg requires boxing in the call to appExpr
-	bool needsBoxing( Type * param, Type * arg, ApplicationExpr * appExpr, TypeSubstitution * env );
+	bool needsBoxing( Type * param, Type * arg, ApplicationExpr * appExpr, const TypeSubstitution * env );
 
 	/// Adds the type variable `tyVar` to `tyVarMap`
Index: src/GenPoly/InstantiateGeneric.cc
===================================================================
--- src/GenPoly/InstantiateGeneric.cc	(revision 1c9568f0e5ea366b2f5bcb84dad6b74b891993d6)
+++ src/GenPoly/InstantiateGeneric.cc	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
@@ -168,5 +168,5 @@
 
 	/// Mutator pass that replaces concrete instantiations of generic types with actual struct declarations, scoped appropriately
-	struct GenericInstantiator final : public WithTypeSubstitution, public WithDeclsToAdd, public WithVisitorRef<GenericInstantiator>, public WithGuards {
+	struct GenericInstantiator final : public WithConstTypeSubstitution, public WithDeclsToAdd, public WithVisitorRef<GenericInstantiator>, public WithGuards {
 		/// Map of (generic type, parameter list) pairs to concrete type instantiations
 		InstantiationMap< AggregateDecl, AggregateDecl > instantiations;
Index: src/GenPoly/Specialize.cc
===================================================================
--- src/GenPoly/Specialize.cc	(revision 1c9568f0e5ea366b2f5bcb84dad6b74b891993d6)
+++ src/GenPoly/Specialize.cc	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
@@ -42,5 +42,5 @@
 
 namespace GenPoly {
-	struct Specialize final : public WithTypeSubstitution, public WithStmtsToAdd, public WithVisitorRef<Specialize> {
+	struct Specialize final : public WithConstTypeSubstitution, public WithStmtsToAdd, public WithVisitorRef<Specialize> {
 		Expression * postmutate( ApplicationExpr *applicationExpr );
 		Expression * postmutate( CastExpr *castExpr );
@@ -54,5 +54,5 @@
 
 	/// Looks up open variables in actual type, returning true if any of them are bound in the environment or formal type.
-	bool needsPolySpecialization( Type *formalType, Type *actualType, TypeSubstitution *env ) {
+	bool needsPolySpecialization( Type *formalType, Type *actualType, const TypeSubstitution *env ) {
 		if ( env ) {
 			using namespace ResolvExpr;
@@ -145,5 +145,5 @@
 	}
 
-	bool needsSpecialization( Type *formalType, Type *actualType, TypeSubstitution *env ) {
+	bool needsSpecialization( Type *formalType, Type *actualType, const TypeSubstitution *env ) {
 		return needsPolySpecialization( formalType, actualType, env ) || needsTupleSpecialization( formalType, actualType );
 	}
Index: src/InitTweak/FixInit.cc
===================================================================
--- src/InitTweak/FixInit.cc	(revision 1c9568f0e5ea366b2f5bcb84dad6b74b891993d6)
+++ src/InitTweak/FixInit.cc	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
@@ -72,5 +72,5 @@
 		};
 
-		struct InsertImplicitCalls : public WithTypeSubstitution {
+		struct InsertImplicitCalls : public WithConstTypeSubstitution {
 			/// wrap function application expressions as ImplicitCopyCtorExpr nodes so that it is easy to identify which
 			/// function calls need their parameters to be copy constructed
@@ -187,5 +187,5 @@
 		};
 
-		class FixCopyCtors final : public WithStmtsToAdd, public WithShortCircuiting, public WithVisitorRef<FixCopyCtors>, public WithTypeSubstitution {
+		class FixCopyCtors final : public WithStmtsToAdd, public WithShortCircuiting, public WithVisitorRef<FixCopyCtors>, public WithConstTypeSubstitution {
 		  public:
 			FixCopyCtors( UnqCount & unqCount ) : unqCount( unqCount ){}
Index: src/SynTree/TypeSubstitution.cc
===================================================================
--- src/SynTree/TypeSubstitution.cc	(revision 1c9568f0e5ea366b2f5bcb84dad6b74b891993d6)
+++ src/SynTree/TypeSubstitution.cc	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
@@ -108,6 +108,7 @@
 namespace {
 	struct EnvTrimmer {
-		TypeSubstitution * env, * newEnv;
-		EnvTrimmer( TypeSubstitution * env, TypeSubstitution * newEnv ) : env( env ), newEnv( newEnv ){}
+		const TypeSubstitution * env;
+		TypeSubstitution * newEnv;
+		EnvTrimmer( const TypeSubstitution * env, TypeSubstitution * newEnv ) : env( env ), newEnv( newEnv ){}
 		void previsit( TypeDecl * tyDecl ) {
 			// transfer known bindings for seen type variables
@@ -120,5 +121,5 @@
 
 /// reduce environment to just the parts that are referenced in a given expression
-TypeSubstitution * TypeSubstitution::newFromExpr( Expression * expr, TypeSubstitution * env ) {
+TypeSubstitution * TypeSubstitution::newFromExpr( Expression * expr, const TypeSubstitution * env ) {
 	if ( env ) {
 		TypeSubstitution * newEnv = new TypeSubstitution();
Index: src/SynTree/TypeSubstitution.h
===================================================================
--- src/SynTree/TypeSubstitution.h	(revision 1c9568f0e5ea366b2f5bcb84dad6b74b891993d6)
+++ src/SynTree/TypeSubstitution.h	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
@@ -39,6 +39,6 @@
 	TypeSubstitution &operator=( const TypeSubstitution &other );
 
-	template< typename SynTreeClass > int apply( SynTreeClass *&input );
-	template< typename SynTreeClass > int applyFree( SynTreeClass *&input );
+	template< typename SynTreeClass > int apply( SynTreeClass *&input ) const;
+	template< typename SynTreeClass > int applyFree( SynTreeClass *&input ) const;
 
 	void add( std::string formalType, Type *actualType );
@@ -56,5 +56,5 @@
 
 	/// create a new TypeSubstitution using bindings from env containing all of the type variables in expr
-	static TypeSubstitution * newFromExpr( Expression * expr, TypeSubstitution * env );
+	static TypeSubstitution * newFromExpr( Expression * expr, const TypeSubstitution * env );
 
 	void normalize();
@@ -130,5 +130,5 @@
 // definitition must happen after PassVisitor is included so that WithGuards can be used
 struct TypeSubstitution::Substituter : public WithGuards, public WithVisitorRef<Substituter> {
-		Substituter( TypeSubstitution & sub, bool freeOnly ) : sub( sub ), freeOnly( freeOnly ) {}
+		Substituter( const TypeSubstitution & sub, bool freeOnly ) : sub( sub ), freeOnly( freeOnly ) {}
 
 		Type * postmutate( TypeInstType * aggregateUseType );
@@ -143,5 +143,5 @@
 		void premutate( UnionInstType * aggregateUseType );
 
-		TypeSubstitution & sub;
+		const TypeSubstitution & sub;
 		int subCount = 0;
 		bool freeOnly;
@@ -151,5 +151,5 @@
 
 template< typename SynTreeClass >
-int TypeSubstitution::apply( SynTreeClass *&input ) {
+int TypeSubstitution::apply( SynTreeClass *&input ) const {
 	assert( input );
 	PassVisitor<Substituter> sub( *this, false );
@@ -163,5 +163,5 @@
 
 template< typename SynTreeClass >
-int TypeSubstitution::applyFree( SynTreeClass *&input ) {
+int TypeSubstitution::applyFree( SynTreeClass *&input ) const {
 	assert( input );
 	PassVisitor<Substituter> sub( *this, true );
Index: src/Tuples/TupleExpansion.cc
===================================================================
--- src/Tuples/TupleExpansion.cc	(revision 1c9568f0e5ea366b2f5bcb84dad6b74b891993d6)
+++ src/Tuples/TupleExpansion.cc	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
@@ -58,5 +58,5 @@
 		};
 
-		struct TupleTypeReplacer : public WithDeclsToAdd, public WithGuards, public WithTypeSubstitution {
+		struct TupleTypeReplacer : public WithDeclsToAdd, public WithGuards, public WithConstTypeSubstitution {
 			Type * postmutate( TupleType * tupleType );
 
Index: src/main.cc
===================================================================
--- src/main.cc	(revision 1c9568f0e5ea366b2f5bcb84dad6b74b891993d6)
+++ src/main.cc	(revision b1d3ee1e84145955b10068d160898445cc59b4a6)
@@ -446,5 +446,5 @@
 }; // description
 
-static_assert( sizeof( long_opts ) / sizeof( long_opts[0] ) - 1 == sizeof( description ) / sizeof( description[0] ) );
+static_assert( sizeof( long_opts ) / sizeof( long_opts[0] ) - 1 == sizeof( description ) / sizeof( description[0] ), "Long opts and description must match" );
 
 static struct Printopts {
