Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision b2a11ba447557f947bbe54300e4b544d986e05af)
+++ src/AST/Convert.cpp	(revision ca0e0a9c8e8e4be0c6155574ef92032483148a6f)
@@ -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 ca0e0a9c8e8e4be0c6155574ef92032483148a6f)
@@ -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 ca0e0a9c8e8e4be0c6155574ef92032483148a6f)
@@ -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 ca0e0a9c8e8e4be0c6155574ef92032483148a6f)
@@ -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 ca0e0a9c8e8e4be0c6155574ef92032483148a6f)
@@ -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 ca0e0a9c8e8e4be0c6155574ef92032483148a6f)
@@ -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 ca0e0a9c8e8e4be0c6155574ef92032483148a6f)
@@ -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 ca0e0a9c8e8e4be0c6155574ef92032483148a6f)
@@ -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
