Index: doc/proposals/function_type_change.md
===================================================================
--- doc/proposals/function_type_change.md	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
+++ doc/proposals/function_type_change.md	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -0,0 +1,34 @@
+## Eliminate Variable Declarations in Function Type ##
+
+The parameters of a function had been living in the wrong place.
+
+As the function type has no relation with the actual declarations of the variables, but only the types of them, putting declarations in FunctionType is unnecessary.
+Meanwhile, in new-ast data model, the declaration nodes should be kept as unique as possible, since they semantically denote unique objects in the source code. Shared declarations often lead to undesirable behaviors, especially when weak references exist (reminder: currently weak references only point to declarations). They also pose difficulty for implementing correct _functional_ algorithms, as copying a declaration node _should_ always mean creating a new entity.
+In the programming language and type theory model, declarations are also never a part of function type; the functions `int f(int a)` and `int f(int b)` have the exact same type (int)->(int), and representing the type as (int a)->(int b) is misleading.
+
+
+## Summary of Changes ##
+
+- `ast::FunctionDecl`
+Now owns its parameter and return variables directly.
+
+- `ast::FunctionType`
+Parameter and return types are now pure types (no more decls)
+Forall clause is part of type information so it is still kept.
+
+- Unify.cc
+Renamed some functions to reflect the changes (decl -> type)
+
+- Convert.cpp
+Drop decls in function type, unless it is directly in function decl (move them to `FunctionDecl` params and returns)
+Add dummy variable decls while converting back.
+
+## Relevant Clean-up Work ##
+
+- CurrentObject.cpp
+No longer has weak references to type nodes and replaced by raw pointers. Using weak pointers do not accomplish anything since a non in-place mutation outside invalidates current iterator anyways and an in-place mutation outside is still seen by the iterator with just a raw pointer.
+
+- Validate.cc
+`EnumAndPointerDecay` is redundant in `resolveTypeof` and therefore dropped.
+Note: this pass needs some structural change to accommodate the new function type representation.
+
Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/AST/Convert.cpp	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -177,9 +177,25 @@
 	const ast::DeclWithType * visit( const ast::FunctionDecl * node ) override final {
 		if ( inCache( node ) ) return nullptr;
+
+		// function decl contains real variables that the type must use.
+		// the structural change means function type in and out of decl
+		// must be handled **differently** on convert back to old.
+		auto ftype = new FunctionType(
+			cv(node->type),
+			(bool)node->type->isVarArgs
+		);
+		ftype->returnVals = get<DeclarationWithType>().acceptL(node->returns);
+		ftype->parameters = get<DeclarationWithType>().acceptL(node->params);
+
+		ftype->forall = get<TypeDecl>().acceptL( node->type->forall );
+
+		visitType(node->type, ftype);
+
 		auto decl = new FunctionDecl(
 			node->name,
 			Type::StorageClasses( node->storage.val ),
 			LinkageSpec::Spec( node->linkage.val ),
-			get<FunctionType>().accept1( node->type ),
+			ftype,
+			//get<FunctionType>().accept1( node->type ),
 			{},
 			get<Attribute>().acceptL( node->attributes ),
@@ -1152,10 +1168,28 @@
 
 	const ast::Type * visit( const ast::FunctionType * node ) override final {
+		static std::string dummy_paramvar_prefix = "__param_";
+		static std::string dummy_returnvar_prefix = "__retval_";
+
 		auto ty = new FunctionType {
 			cv( node ),
 			(bool)node->isVarArgs
 		};
-		ty->returnVals = get<DeclarationWithType>().acceptL( node->returns );
-		ty->parameters = get<DeclarationWithType>().acceptL( node->params );
+		auto returns = get<Type>().acceptL(node->returns);
+		auto params = get<Type>().acceptL(node->params);
+
+		int ret_index = 0;
+		for (auto t: returns) {
+			// xxx - LinkageSpec shouldn't matter but needs to be something
+			ObjectDecl * dummy = new ObjectDecl(dummy_returnvar_prefix + std::to_string(ret_index++), {}, LinkageSpec::C, nullptr, t, nullptr);
+			ty->returnVals.push_back(dummy);
+		}
+		int param_index = 0;
+		for (auto t: params) {
+			ObjectDecl * dummy = new ObjectDecl(dummy_paramvar_prefix + std::to_string(param_index++), {}, LinkageSpec::C, nullptr, t, nullptr);
+			ty->parameters.push_back(dummy);
+		}
+
+		// ty->returnVals = get<DeclarationWithType>().acceptL( node->returns );
+		// ty->parameters = get<DeclarationWithType>().acceptL( node->params );
 		ty->forall = get<TypeDecl>().acceptL( node->forall );
 		return visitType( node, ty );
@@ -1374,5 +1408,9 @@
 	ast::Node * node = nullptr;
 	/// cache of nodes that might be referenced by readonly<> for de-duplication
-	std::unordered_map< const BaseSyntaxNode *, ast::Node * > cache = {};
+	/// in case that some nodes are dropped by conversion (due to possible structural change)
+	/// use smart pointers in cache value to prevent accidental invalidation.
+	/// at conversion stage, all created nodes are guaranteed to be unique, therefore
+	/// const_casting out of smart pointers is permitted.
+	std::unordered_map< const BaseSyntaxNode *, ast::ptr<ast::Node> > cache = {};
 
 	// Local Utilities:
@@ -1447,5 +1485,5 @@
 		auto it = cache.find( old );
 		if ( it == cache.end() ) return false;
-		node = it->second;
+		node = const_cast<ast::Node *>(it->second.get());
 		return true;
 	}
@@ -1486,8 +1524,28 @@
 	virtual void visit( const FunctionDecl * old ) override final {
 		if ( inCache( old ) ) return;
+		auto paramVars = GET_ACCEPT_V(type->parameters, DeclWithType);
+		auto returnVars = GET_ACCEPT_V(type->returnVals, DeclWithType);
+		auto forall = GET_ACCEPT_V(type->forall, TypeDecl);
+
+		// function type is now derived from parameter decls instead of storing them
+		auto ftype = new ast::FunctionType((ast::ArgumentFlag)old->type->isVarArgs, cv(old->type));
+		ftype->params.reserve(paramVars.size());
+		ftype->returns.reserve(returnVars.size());
+
+		for (auto & v: paramVars) {
+			ftype->params.emplace_back(v->get_type());
+		}
+		for (auto & v: returnVars) {
+			ftype->returns.emplace_back(v->get_type());
+		}
+		ftype->forall = std::move(forall);
+		visitType(old->type, ftype);
+
 		auto decl = new ast::FunctionDecl{
 			old->location,
 			old->name,
-			GET_ACCEPT_1(type, FunctionType),
+			// GET_ACCEPT_1(type, FunctionType),
+			std::move(paramVars),
+			std::move(returnVars),
 			{},
 			{ old->storageClasses.val },
@@ -1496,5 +1554,8 @@
 			{ old->get_funcSpec().val }
 		};
+
+		decl->type = ftype;
 		cache.emplace( old, decl );
+
 		decl->withExprs = GET_ACCEPT_V(withExprs, Expr);
 		decl->stmts = GET_ACCEPT_1(statements, CompoundStmt);
@@ -2515,6 +2576,14 @@
 			cv( old )
 		};
-		ty->returns = GET_ACCEPT_V( returnVals, DeclWithType );
-		ty->params = GET_ACCEPT_V( parameters, DeclWithType );
+		auto returnVars = GET_ACCEPT_V(returnVals, DeclWithType);
+		auto paramVars = GET_ACCEPT_V(parameters, DeclWithType);
+		// ty->returns = GET_ACCEPT_V( returnVals, DeclWithType );
+		// ty->params = GET_ACCEPT_V( parameters, DeclWithType );
+		for (auto & v: returnVars) {
+			ty->returns.emplace_back(v->get_type());
+		}
+		for (auto & v: paramVars) {
+			ty->params.emplace_back(v->get_type());
+		}
 		ty->forall = GET_ACCEPT_V( forall, TypeDecl );
 		visitType( old, ty );
Index: src/AST/Decl.hpp
===================================================================
--- src/AST/Decl.hpp	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/AST/Decl.hpp	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -124,12 +124,16 @@
 class FunctionDecl : public DeclWithType {
 public:
+	std::vector<ptr<DeclWithType>> params;
+	std::vector<ptr<DeclWithType>> returns;
+	// declared type, derived from parameter declarations
 	ptr<FunctionType> type;
 	ptr<CompoundStmt> stmts;
 	std::vector< ptr<Expr> > withExprs;
 
-	FunctionDecl( const CodeLocation & loc, const std::string & name, FunctionType * type,
+	FunctionDecl( const CodeLocation & loc, const std::string & name, 
+		std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,
 		CompoundStmt * stmts, Storage::Classes storage = {}, Linkage::Spec linkage = Linkage::C,
 		std::vector<ptr<Attribute>>&& attrs = {}, Function::Specs fs = {})
-	: DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), type( type ),
+	: DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)),
 	  stmts( stmts ) {}
 
Index: src/AST/ForallSubstitutor.hpp
===================================================================
--- src/AST/ForallSubstitutor.hpp	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/AST/ForallSubstitutor.hpp	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -33,4 +33,14 @@
 	}
 
+	template<typename node_t > 
+	std::vector<ptr<node_t>> operator() (const std::vector<ptr<node_t>> & o) {
+		std::vector<ptr<node_t>> n;
+		n.reserve(o.size());
+		for (const node_t * d : o) { n.emplace_back(d->accept(*visitor)); }
+		return n;
+	}
+	
+	/*
+
 	/// Substitute parameter/return type
 	std::vector< ptr< DeclWithType > > operator() ( const std::vector< ptr< DeclWithType > > & o ) {
@@ -48,4 +58,6 @@
 		return n;
 	}
+
+	*/
 };
 
Index: src/AST/Pass.impl.hpp
===================================================================
--- src/AST/Pass.impl.hpp	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/AST/Pass.impl.hpp	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -465,4 +465,8 @@
 			__pass::symtab::addId( core, 0, func );
 			VISIT(
+				// parameter declarations are now directly here
+				maybe_accept( node, &FunctionDecl::params );
+				maybe_accept( node, &FunctionDecl::returns );
+				// foralls are still in function type
 				maybe_accept( node, &FunctionDecl::type );
 				// function body needs to have the same scope as parameters - CompoundStmt will not enter
Index: src/AST/SymbolTable.cpp
===================================================================
--- src/AST/SymbolTable.cpp	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/AST/SymbolTable.cpp	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -335,4 +335,5 @@
 }
 
+/*
 void SymbolTable::addFunctionType( const FunctionType * ftype ) {
 	addTypes( ftype->forall );
@@ -340,4 +341,5 @@
 	addIds( ftype->params );
 }
+*/
 
 void SymbolTable::lazyInitScope() {
@@ -368,5 +370,5 @@
 		assert( ! params.empty() );
 		// use base type of pointer, so that qualifiers on the pointer type aren't considered.
-		const Type * base = InitTweak::getPointerBase( params.front()->get_type() );
+		const Type * base = InitTweak::getPointerBase( params.front() );
 		assert( base );
 		return Mangle::mangle( base );
Index: src/AST/SymbolTable.hpp
===================================================================
--- src/AST/SymbolTable.hpp	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/AST/SymbolTable.hpp	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -145,5 +145,5 @@
 
 	/// convenience function for adding all of the declarations in a function type to the indexer
-	void addFunctionType( const FunctionType * ftype );
+	// void addFunctionType( const FunctionType * ftype );
 
 private:
Index: src/AST/Type.cpp
===================================================================
--- src/AST/Type.cpp	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/AST/Type.cpp	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -102,4 +102,5 @@
 // --- FunctionType
 
+
 FunctionType::FunctionType( const FunctionType & o )
 : ParameterizedType( o.qualifiers, copy( o.attributes ) ), returns(), params(),
@@ -112,7 +113,7 @@
 
 namespace {
-	bool containsTtype( const std::vector<ptr<DeclWithType>> & l ) {
+	bool containsTtype( const std::vector<ptr<Type>> & l ) {
 		if ( ! l.empty() ) {
-			return Tuples::isTtype( l.back()->get_type() );
+			return Tuples::isTtype( l.back() );
 		}
 		return false;
Index: src/AST/Type.hpp
===================================================================
--- src/AST/Type.hpp	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/AST/Type.hpp	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -302,6 +302,9 @@
 class FunctionType final : public ParameterizedType {
 public:
-	std::vector<ptr<DeclWithType>> returns;
-	std::vector<ptr<DeclWithType>> params;
+//	std::vector<ptr<DeclWithType>> returns;
+//	std::vector<ptr<DeclWithType>> params;
+
+	std::vector<ptr<Type>> returns;
+	std::vector<ptr<Type>> params;
 
 	/// Does the function accept a variable number of arguments following the arguments specified
Index: src/InitTweak/InitTweak.cc
===================================================================
--- src/InitTweak/InitTweak.cc	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/InitTweak/InitTweak.cc	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -1026,7 +1026,7 @@
 		if ( ftype->params.size() != 2 ) return false;
 
-		const ast::Type * t1 = getPointerBase( ftype->params.front()->get_type() );
+		const ast::Type * t1 = getPointerBase( ftype->params.front() );
 		if ( ! t1 ) return false;
-		const ast::Type * t2 = ftype->params.back()->get_type();
+		const ast::Type * t2 = ftype->params.back();
 
 		return ResolvExpr::typesCompatibleIgnoreQualifiers( t1, t2, ast::SymbolTable{} );
Index: src/ResolvExpr/CandidateFinder.cpp
===================================================================
--- src/ResolvExpr/CandidateFinder.cpp	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/ResolvExpr/CandidateFinder.cpp	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -188,10 +188,10 @@
 
 			// mark conversion cost and also specialization cost of param type
-			const ast::Type * paramType = (*param)->get_type();
+			// const ast::Type * paramType = (*param)->get_type();
 			cand->expr = ast::mutate_field_index(
 				appExpr, &ast::ApplicationExpr::args, i,
 				computeExpressionConversionCost(
-					args[i], paramType, symtab, cand->env, convCost ) );
-			convCost.decSpec( specCost( paramType ) );
+					args[i], *param, symtab, cand->env, convCost ) );
+			convCost.decSpec( specCost( *param ) );
 			++param;  // can't be in for-loop update because of the continue
 		}
@@ -698,5 +698,5 @@
 			if ( targetType && ! targetType->isVoid() && ! funcType->returns.empty() ) {
 				// attempt to narrow based on expected target type
-				const ast::Type * returnType = funcType->returns.front()->get_type();
+				const ast::Type * returnType = funcType->returns.front();
 				if ( ! unify(
 					returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, symtab )
@@ -712,12 +712,28 @@
 			std::size_t genStart = 0;
 
-			for ( const ast::DeclWithType * param : funcType->params ) {
-				auto obj = strict_dynamic_cast< const ast::ObjectDecl * >( param );
+			// xxx - how to handle default arg after change to ftype representation?
+			if (const ast::VariableExpr * varExpr = func->expr.as<ast::VariableExpr>()) {
+				if (const ast::FunctionDecl * funcDecl = varExpr->var.as<ast::FunctionDecl>()) {
+					// function may have default args only if directly calling by name
+					// must use types on candidate however, due to RenameVars substitution
+					auto nParams = funcType->params.size();
+
+					for (size_t i=0; i<nParams; ++i) {
+						auto obj = funcDecl->params[i].strict_as<ast::ObjectDecl>();
+						if (!instantiateArgument(
+							funcType->params[i], obj->init, args, results, genStart, symtab)) return;
+					}
+					goto endMatch;
+				}
+			}
+			for ( const auto & param : funcType->params ) {
 				// Try adding the arguments corresponding to the current parameter to the existing
 				// matches
+				// no default args for indirect calls
 				if ( ! instantiateArgument(
-					obj->type, obj->init, args, results, genStart, symtab ) ) return;
-			}
-
+					param, nullptr, args, results, genStart, symtab ) ) return;
+			}
+
+			endMatch:
 			if ( funcType->isVarArgs ) {
 				// append any unused arguments to vararg pack
Index: src/ResolvExpr/CurrentObject.cc
===================================================================
--- src/ResolvExpr/CurrentObject.cc	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/ResolvExpr/CurrentObject.cc	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -594,5 +594,5 @@
 	class SimpleIterator final : public MemberIterator {
 		CodeLocation location;
-		readonly< Type > type = nullptr;
+		const Type * type = nullptr;
 	public:
 		SimpleIterator( const CodeLocation & loc, const Type * t ) : location( loc ), type( t ) {}
@@ -630,6 +630,6 @@
 	class ArrayIterator final : public MemberIterator {
 		CodeLocation location;
-		readonly< ArrayType > array = nullptr;
-		readonly< Type > base = nullptr;
+		const ArrayType * array = nullptr;
+		const Type * base = nullptr;
 		size_t index = 0;
 		size_t size = 0;
Index: src/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/ResolvExpr/Resolver.cc	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -1223,5 +1223,5 @@
 		template<typename Iter>
 		inline bool nextMutex( Iter & it, const Iter & end ) {
-			while ( it != end && ! (*it)->get_type()->is_mutex() ) { ++it; }
+			while ( it != end && ! (*it)->is_mutex() ) { ++it; }
 			return it != end;
 		}
@@ -1638,8 +1638,8 @@
 								// Check if the argument matches the parameter type in the current
 								// scope
-								ast::ptr< ast::Type > paramType = (*param)->get_type();
+								// ast::ptr< ast::Type > paramType = (*param)->get_type();
 								if (
 									! unify(
-										arg->expr->result, paramType, resultEnv, need, have, open,
+										arg->expr->result, *param, resultEnv, need, have, open,
 										symtab )
 								) {
@@ -1648,5 +1648,5 @@
 									ss << "candidate function not viable: no known conversion "
 										"from '";
-									ast::print( ss, (*param)->get_type() );
+									ast::print( ss, *param );
 									ss << "' to '";
 									ast::print( ss, arg->expr->result );
Index: src/ResolvExpr/SatisfyAssertions.cpp
===================================================================
--- src/ResolvExpr/SatisfyAssertions.cpp	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/ResolvExpr/SatisfyAssertions.cpp	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -318,6 +318,6 @@
 					if ( ! func ) continue;
 
-					for ( const ast::DeclWithType * param : func->params ) {
-						cost.decSpec( specCost( param->get_type() ) );
+					for ( const auto & param : func->params ) {
+						cost.decSpec( specCost( param ) );
 					}
 
Index: src/ResolvExpr/SpecCost.cc
===================================================================
--- src/ResolvExpr/SpecCost.cc	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/ResolvExpr/SpecCost.cc	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -178,6 +178,6 @@
 		void previsit( const ast::FunctionType * fty ) {
 			int minCount = std::numeric_limits<int>::max();
-			updateMinimumPresent( minCount, fty->params, decl_type );
-			updateMinimumPresent( minCount, fty->returns, decl_type );
+			updateMinimumPresent( minCount, fty->params, type_deref );
+			updateMinimumPresent( minCount, fty->returns, type_deref );
 			// Add another level to minCount if set.
 			count = toNoneOrInc( minCount );
Index: src/ResolvExpr/Unify.cc
===================================================================
--- src/ResolvExpr/Unify.cc	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/ResolvExpr/Unify.cc	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -395,5 +395,5 @@
 
 	template< typename Iterator1, typename Iterator2 >
-	bool unifyDeclList( Iterator1 list1Begin, Iterator1 list1End, Iterator2 list2Begin, Iterator2 list2End, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, const SymTab::Indexer &indexer ) {
+	bool unifyTypeList( Iterator1 list1Begin, Iterator1 list1End, Iterator2 list2Begin, Iterator2 list2End, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, const SymTab::Indexer &indexer ) {
 		auto get_type = [](DeclarationWithType * dwt){ return dwt->get_type(); };
 		for ( ; list1Begin != list1End && list2Begin != list2End; ++list1Begin, ++list2Begin ) {
@@ -489,6 +489,6 @@
 					|| flatOther->isTtype()
 			) {
-				if ( unifyDeclList( flatFunc->parameters.begin(), flatFunc->parameters.end(), flatOther->parameters.begin(), flatOther->parameters.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {
-					if ( unifyDeclList( flatFunc->returnVals.begin(), flatFunc->returnVals.end(), flatOther->returnVals.begin(), flatOther->returnVals.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {
+				if ( unifyTypeList( flatFunc->parameters.begin(), flatFunc->parameters.end(), flatOther->parameters.begin(), flatOther->parameters.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {
+					if ( unifyTypeList( flatFunc->returnVals.begin(), flatFunc->returnVals.end(), flatOther->returnVals.begin(), flatOther->returnVals.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {
 
 						// the original types must be used in mark assertions, since pointer comparisons are used
@@ -784,15 +784,15 @@
 
 		/// returns flattened version of `src`
-		static std::vector< ast::ptr< ast::DeclWithType > > flattenList(
-			const std::vector< ast::ptr< ast::DeclWithType > > & src, ast::TypeEnvironment & env
+		static std::vector< ast::ptr< ast::Type > > flattenList(
+			const std::vector< ast::ptr< ast::Type > > & src, ast::TypeEnvironment & env
 		) {
-			std::vector< ast::ptr< ast::DeclWithType > > dst;
+			std::vector< ast::ptr< ast::Type > > dst;
 			dst.reserve( src.size() );
-			for ( const ast::DeclWithType * d : src ) {
+			for ( const auto & d : src ) {
 				ast::Pass<TtypeExpander_new> expander{ env };
 				// TtypeExpander pass is impure (may mutate nodes in place)
 				// need to make nodes shared to prevent accidental mutation
-				ast::ptr<ast::DeclWithType> dc = d->accept(expander);
-				auto types = flatten( dc->get_type() );
+				ast::ptr<ast::Type> dc = d->accept(expander);
+				auto types = flatten( dc );
 				for ( ast::ptr< ast::Type > & t : types ) {
 					// outermost const, volatile, _Atomic qualifiers in parameters should not play
@@ -803,5 +803,5 @@
 					// requirements than a non-mutex function
 					remove_qualifiers( t, ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic );
-					dst.emplace_back( new ast::ObjectDecl{ dc->location, "", t } );
+					dst.emplace_back( t );
 				}
 			}
@@ -811,10 +811,10 @@
 		/// Creates a tuple type based on a list of DeclWithType
 		template< typename Iter >
-		static ast::ptr< ast::Type > tupleFromDecls( Iter crnt, Iter end ) {
+		static ast::ptr< ast::Type > tupleFromTypes( Iter crnt, Iter end ) {
 			std::vector< ast::ptr< ast::Type > > types;
 			while ( crnt != end ) {
 				// it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure
 				// that this results in a flat tuple
-				flatten( (*crnt)->get_type(), types );
+				flatten( *crnt, types );
 
 				++crnt;
@@ -825,5 +825,5 @@
 
 		template< typename Iter >
-		static bool unifyDeclList(
+		static bool unifyTypeList(
 			Iter crnt1, Iter end1, Iter crnt2, Iter end2, ast::TypeEnvironment & env,
 			ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
@@ -831,6 +831,6 @@
 		) {
 			while ( crnt1 != end1 && crnt2 != end2 ) {
-				const ast::Type * t1 = (*crnt1)->get_type();
-				const ast::Type * t2 = (*crnt2)->get_type();
+				const ast::Type * t1 = *crnt1;
+				const ast::Type * t2 = *crnt2;
 				bool isTuple1 = Tuples::isTtype( t1 );
 				bool isTuple2 = Tuples::isTtype( t2 );
@@ -840,10 +840,10 @@
 					// combine remainder of list2, then unify
 					return unifyExact(
-						t1, tupleFromDecls( crnt2, end2 ), env, need, have, open,
+						t1, tupleFromTypes( crnt2, end2 ), env, need, have, open,
 						noWiden(), symtab );
 				} else if ( ! isTuple1 && isTuple2 ) {
 					// combine remainder of list1, then unify
 					return unifyExact(
-						tupleFromDecls( crnt1, end1 ), t2, env, need, have, open,
+						tupleFromTypes( crnt1, end1 ), t2, env, need, have, open,
 						noWiden(), symtab );
 				}
@@ -860,15 +860,15 @@
 			if ( crnt1 != end1 ) {
 				// try unifying empty tuple with ttype
-				const ast::Type * t1 = (*crnt1)->get_type();
+				const ast::Type * t1 = *crnt1;
 				if ( ! Tuples::isTtype( t1 ) ) return false;
 				return unifyExact(
-					t1, tupleFromDecls( crnt2, end2 ), env, need, have, open,
+					t1, tupleFromTypes( crnt2, end2 ), env, need, have, open,
 					noWiden(), symtab );
 			} else if ( crnt2 != end2 ) {
 				// try unifying empty tuple with ttype
-				const ast::Type * t2 = (*crnt2)->get_type();
+				const ast::Type * t2 = *crnt2;
 				if ( ! Tuples::isTtype( t2 ) ) return false;
 				return unifyExact(
-					tupleFromDecls( crnt1, end1 ), t2, env, need, have, open,
+					tupleFromTypes( crnt1, end1 ), t2, env, need, have, open,
 					noWiden(), symtab );
 			}
@@ -877,11 +877,11 @@
 		}
 
-		static bool unifyDeclList(
-			const std::vector< ast::ptr< ast::DeclWithType > > & list1,
-			const std::vector< ast::ptr< ast::DeclWithType > > & list2,
+		static bool unifyTypeList(
+			const std::vector< ast::ptr< ast::Type > > & list1,
+			const std::vector< ast::ptr< ast::Type > > & list2,
 			ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
 			const ast::OpenVarSet & open, const ast::SymbolTable & symtab
 		) {
-			return unifyDeclList(
+			return unifyTypeList(
 				list1.begin(), list1.end(), list2.begin(), list2.end(), env, need, have, open,
 				symtab );
@@ -928,6 +928,6 @@
 			) return;
 
-			if ( ! unifyDeclList( params, params2, tenv, need, have, open, symtab ) ) return;
-			if ( ! unifyDeclList(
+			if ( ! unifyTypeList( params, params2, tenv, need, have, open, symtab ) ) return;
+			if ( ! unifyTypeList(
 				func->returns, func2->returns, tenv, need, have, open, symtab ) ) return;
 
@@ -1232,9 +1232,9 @@
 	ast::ptr<ast::Type> extractResultType( const ast::FunctionType * func ) {
 		if ( func->returns.empty() ) return new ast::VoidType{};
-		if ( func->returns.size() == 1 ) return func->returns[0]->get_type();
+		if ( func->returns.size() == 1 ) return func->returns[0];
 
 		std::vector<ast::ptr<ast::Type>> tys;
-		for ( const ast::DeclWithType * decl : func->returns ) {
-			tys.emplace_back( decl->get_type() );
+		for ( const auto & decl : func->returns ) {
+			tys.emplace_back( decl );
 		}
 		return new ast::TupleType{ std::move(tys) };
Index: src/SymTab/Mangler.cc
===================================================================
--- src/SymTab/Mangler.cc	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/SymTab/Mangler.cc	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -551,10 +551,8 @@
 			GuardValue( inFunctionType );
 			inFunctionType = true;
-			std::vector< ast::ptr< ast::Type > > returnTypes = getTypes( functionType->returns );
-			if (returnTypes.empty()) mangleName << Encoding::void_t;
-			else accept_each( returnTypes, *visitor );
+			if (functionType->returns.empty()) mangleName << Encoding::void_t;
+			else accept_each( functionType->returns, *visitor );
 			mangleName << "_";
-			std::vector< ast::ptr< ast::Type > > paramTypes = getTypes( functionType->params );
-			accept_each( paramTypes, *visitor );
+			accept_each( functionType->params, *visitor );
 			mangleName << "_";
 		}
Index: src/SymTab/Validate.cc
===================================================================
--- src/SymTab/Validate.cc	(revision eccb14d7241424d1aae22fb905858900c58eaa4e)
+++ src/SymTab/Validate.cc	(revision 954c954fd6522dace325fb95ef0cfd3247c319c3)
@@ -1384,4 +1384,5 @@
 	/// Replaces enum types by int, and function/array types in function parameter and return
 	/// lists by appropriate pointers
+	/*
 	struct EnumAndPointerDecay_new {
 		const ast::EnumDecl * previsit( const ast::EnumDecl * enumDecl ) {
@@ -1434,4 +1435,5 @@
 		}
 	};
+	*/
 
 	/// expand assertions from a trait instance, performing appropriate type variable substitutions
@@ -1837,9 +1839,9 @@
 const ast::Type * validateType(
 		const CodeLocation & loc, const ast::Type * type, const ast::SymbolTable & symtab ) {
-	ast::Pass< EnumAndPointerDecay_new > epc;
+	// ast::Pass< EnumAndPointerDecay_new > epc;
 	ast::Pass< LinkReferenceToTypes_new > lrt{ loc, symtab };
 	ast::Pass< ForallPointerDecay_new > fpd{ loc };
 
-	return type->accept( epc )->accept( lrt )->accept( fpd );
+	return type->accept( lrt )->accept( fpd );
 }
 
