Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/AST/Convert.cpp	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/AST/Decl.hpp	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/AST/ForallSubstitutor.hpp	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/AST/Pass.impl.hpp	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/AST/SymbolTable.cpp	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/AST/SymbolTable.hpp	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/AST/Type.cpp	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/AST/Type.hpp	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/InitTweak/InitTweak.cc	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/ResolvExpr/CandidateFinder.cpp	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/ResolvExpr/CurrentObject.cc	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/ResolvExpr/Resolver.cc	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/ResolvExpr/SatisfyAssertions.cpp	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/ResolvExpr/SpecCost.cc	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/ResolvExpr/Unify.cc	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/SymTab/Mangler.cc	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/SymTab/Validate.cc	(revision 08e88514b1a5b79ea52c8bcb7e26e07cb0e75567)
@@ -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 );
 }
 
