Index: src/AST/Attribute.cpp
===================================================================
--- src/AST/Attribute.cpp	(revision 59c8dff49bbb5284b39ab27da894a644327a531a)
+++ src/AST/Attribute.cpp	(revision f988834af650698a46072a2a91bb190224a866bd)
@@ -38,9 +38,9 @@
 
 bool Attribute::isValidOnFuncParam() const {
-	// attributes such as aligned, cleanup, etc. produce GCC errors when they appear
-	// on function parameters. Maintain here a whitelist of attribute names that are
-	// allowed to appear on parameters.
+	// Attributes produce GCC errors when they appear on function
+	// parameters. Names on the previous allow-list implementation:
+	// unused, noreturn, __vector_size__
 	std::string norm = normalizedName();
-	return norm == "unused" || norm == "noreturn";
+	return norm != "aligned" && norm != "packed" && norm != "used";
 }
 
Index: src/AST/Decl.hpp
===================================================================
--- src/AST/Decl.hpp	(revision 59c8dff49bbb5284b39ab27da894a644327a531a)
+++ src/AST/Decl.hpp	(revision f988834af650698a46072a2a91bb190224a866bd)
@@ -29,5 +29,4 @@
 #include "StorageClasses.hpp"
 #include "Visitor.hpp"
-#include "Common/utility.h"
 
 // Must be included in *all* AST classes; should be #undef'd at the end of the file
Index: src/AST/Pass.proto.hpp
===================================================================
--- src/AST/Pass.proto.hpp	(revision 59c8dff49bbb5284b39ab27da894a644327a531a)
+++ src/AST/Pass.proto.hpp	(revision f988834af650698a46072a2a91bb190224a866bd)
@@ -19,4 +19,5 @@
 #include "Common/Iterate.hpp"
 #include "Common/Stats/Heap.h"
+#include "Common/utility.h"
 namespace ast {
 	template<typename core_t> class Pass;
Index: src/CodeGen/CodeGenerator.hpp
===================================================================
--- src/CodeGen/CodeGenerator.hpp	(revision 59c8dff49bbb5284b39ab27da894a644327a531a)
+++ src/CodeGen/CodeGenerator.hpp	(revision f988834af650698a46072a2a91bb190224a866bd)
@@ -21,4 +21,5 @@
 #include "AST/Pass.hpp"          // for WithGuards, WithShortCircuiting, ...
 #include "CodeGen/Options.h"     // for Options
+#include "Common/Indenter.h"     // for Indenter
 
 
Index: src/Common/utility.h
===================================================================
--- src/Common/utility.h	(revision 59c8dff49bbb5284b39ab27da894a644327a531a)
+++ src/Common/utility.h	(revision f988834af650698a46072a2a91bb190224a866bd)
@@ -10,6 +10,6 @@
 // Created On       : Mon May 18 07:44:20 2015
 // Last Modified By : Andrew Beach
-// Last Modified On : Fri Feb 17 15:25:00 2023
-// Update Count     : 53
+// Last Modified On : Wed Jan 17 14:40:00 2024
+// Update Count     : 54
 //
 
@@ -17,114 +17,13 @@
 
 #include <cassert>
-#include <cctype>
 #include <algorithm>
-#include <iostream>
 #include <list>
-#include <memory>
 #include <string>
 #include <type_traits>
 #include <vector>
-#include <cstring>										// memcmp
-
-#include "Common/Indenter.h"
-
-class Expression;
-
-/// bring std::move into global scope
-using std::move;
 
 /// partner to move that copies any copyable type
 template<typename T>
 T copy( const T & x ) { return x; }
-
-template< typename T >
-static inline T * maybeClone( const T *orig ) {
-	if ( orig ) {
-		return orig->clone();
-	} else {
-		return 0;
-	} // if
-}
-
-template< typename Input_iterator >
-void printEnums( Input_iterator begin, Input_iterator end, const char * const *name_array, std::ostream &os ) {
-	for ( Input_iterator i = begin; i != end; ++i ) {
-		os << name_array[ *i ] << ' ';
-	} // for
-}
-
-template< typename Container >
-void deleteAll( const Container &container ) {
-	for ( const auto &i : container ) {
-		delete i;
-	} // for
-}
-
-template< typename Container >
-void printAll( const Container &container, std::ostream &os, Indenter indent = {} ) {
-	for ( typename Container::const_iterator i = container.begin(); i != container.end(); ++i ) {
-		if ( *i ) {
-			os << indent;
-			(*i)->print( os, indent );
-			// need an endl after each element because it's not easy to know when each individual item should end
-			os << std::endl;
-		} // if
-	} // for
-}
-
-template< typename SrcContainer, typename DestContainer >
-void cloneAll( const SrcContainer &src, DestContainer &dest ) {
-	typename SrcContainer::const_iterator in = src.begin();
-	std::back_insert_iterator< DestContainer > out( dest );
-	while ( in != src.end() ) {
-		*out++ = (*in++)->clone();
-	} // while
-}
-
-template< typename SrcContainer, typename DestContainer, typename Predicate >
-void cloneAll_if( const SrcContainer &src, DestContainer &dest, Predicate pred ) {
-	std::back_insert_iterator< DestContainer > out( dest );
-	for ( auto x : src ) {
-		if ( pred(x) ) {
-			*out++ = x->clone();
-		}
-	} // while
-}
-
-template< typename Container >
-void assertAll( const Container &container ) {
-	int count = 0;
-	for ( typename Container::const_iterator i = container.begin(); i != container.end(); ++i ) {
-		if ( !(*i) ) {
-			std::cerr << count << " is null" << std::endl;
-		} // if
-	} // for
-}
-
-template < typename T >
-std::list<T> tail( std::list<T> l ) {
-	if ( ! l.empty() ) {
-		std::list<T> ret(++(l.begin()), l.end());
-		return ret;
-	} // if
-}
-
-template < typename T >
-std::list<T> flatten( std::list < std::list<T> > l) {
-	typedef std::list <T> Ts;
-
-	Ts ret;
-
-	switch ( l.size() ) {
-	  case 0:
-		return ret;
-	  case 1:
-		return l.front();
-	  default:
-		ret = flatten(tail(l));
-		ret.insert(ret.begin(), l.front().begin(), l.front().end());
-		return ret;
-	} // switch
-}
 
 /// Splice src onto the end of dst, clearing src
@@ -143,24 +42,5 @@
 }
 
-template< typename... Args >
-auto filter(Args&&... args) -> decltype(std::copy_if(std::forward<Args>(args)...)) {
-  return std::copy_if(std::forward<Args>(args)...);
-}
-
-template <typename E, typename UnaryPredicate, template< typename, typename...> class Container, typename... Args >
-void filter( Container< E *, Args... > & container, UnaryPredicate pred, bool doDelete ) {
-	auto i = begin( container );
-	while ( i != end( container ) ) {
-		auto it = next( i );
-		if ( pred( *i ) ) {
-			if ( doDelete ) {
-				delete *i;
-			} // if
-			container.erase( i );
-		} // if
-		i = it;
-	} // while
-}
-
+/// Remove elements that match pred from the container.
 template<typename Container, typename Pred>
 void erase_if( Container & cont, Pred && pred ) {
Index: src/InitTweak/FixInit.cpp
===================================================================
--- src/InitTweak/FixInit.cpp	(revision 59c8dff49bbb5284b39ab27da894a644327a531a)
+++ src/InitTweak/FixInit.cpp	(revision f988834af650698a46072a2a91bb190224a866bd)
@@ -581,5 +581,7 @@
 	}
 
-	if ( ! dtor->env ) dtor->env = maybeClone( env );
+	if ( nullptr == dtor->env && nullptr != env ) {
+		dtor->env = ast::shallowCopy( env );
+	}
 	auto dtorFunc = getDtorFunc( ret, new ast::ExprStmt(loc, dtor ), stmtsToAddBefore );
 
Index: src/Parser/DeclarationNode.cc
===================================================================
--- src/Parser/DeclarationNode.cc	(revision 59c8dff49bbb5284b39ab27da894a644327a531a)
+++ src/Parser/DeclarationNode.cc	(revision f988834af650698a46072a2a91bb190224a866bd)
@@ -35,5 +35,5 @@
 #include "Common/SemanticError.h"  // for SemanticError
 #include "Common/UniqueName.h"     // for UniqueName
-#include "Common/utility.h"        // for maybeClone
+#include "Common/utility.h"        // for copy, spliceBegin
 #include "Parser/ExpressionNode.h" // for ExpressionNode
 #include "Parser/InitializerNode.h"// for InitializerNode
@@ -41,6 +41,4 @@
 #include "TypeData.h"              // for TypeData, TypeData::Aggregate_t
 #include "TypedefTable.h"          // for TypedefTable
-
-class Initializer;
 
 extern TypedefTable typedefTable;
@@ -101,29 +99,29 @@
 DeclarationNode * DeclarationNode::clone() const {
 	DeclarationNode * newnode = new DeclarationNode;
-	newnode->set_next( maybeClone( get_next() ) );
+	newnode->set_next( maybeCopy( get_next() ) );
 	newnode->name = name ? new string( *name ) : nullptr;
 
 	newnode->builtin = NoBuiltinType;
-	newnode->type = maybeClone( type );
+	newnode->type = maybeCopy( type );
 	newnode->inLine = inLine;
 	newnode->storageClasses = storageClasses;
 	newnode->funcSpecs = funcSpecs;
-	newnode->bitfieldWidth = maybeClone( bitfieldWidth );
-	newnode->enumeratorValue.reset( maybeClone( enumeratorValue.get() ) );
+	newnode->bitfieldWidth = maybeCopy( bitfieldWidth );
+	newnode->enumeratorValue.reset( maybeCopy( enumeratorValue.get() ) );
 	newnode->hasEllipsis = hasEllipsis;
 	newnode->linkage = linkage;
 	newnode->asmName = maybeCopy( asmName );
 	newnode->attributes = attributes;
-	newnode->initializer = maybeClone( initializer );
+	newnode->initializer = maybeCopy( initializer );
 	newnode->extension = extension;
-	newnode->asmStmt = maybeClone( asmStmt );
+	newnode->asmStmt = maybeCopy( asmStmt );
 	newnode->error = error;
 
 //	newnode->variable.name = variable.name ? new string( *variable.name ) : nullptr;
 	newnode->variable.tyClass = variable.tyClass;
-	newnode->variable.assertions = maybeClone( variable.assertions );
-	newnode->variable.initializer = maybeClone( variable.initializer );
-
-	newnode->assert.condition = maybeClone( assert.condition );
+	newnode->variable.assertions = maybeCopy( variable.assertions );
+	newnode->variable.initializer = maybeCopy( variable.initializer );
+
+	newnode->assert.condition = maybeCopy( assert.condition );
 	newnode->assert.message = maybeCopy( assert.message );
 	return newnode;
@@ -664,5 +662,5 @@
 				dst->base->aggInst.aggregate = src;
 				if ( src->kind == TypeData::Aggregate ) {
-					dst->base->aggInst.params = maybeClone( src->aggregate.actuals );
+					dst->base->aggInst.params = maybeCopy( src->aggregate.actuals );
 				} // if
 				dst->base->qualifiers |= src->qualifiers;
@@ -694,5 +692,5 @@
 					if ( o->type->kind == TypeData::Aggregate ) {
 						type->aggInst.hoistType = o->type->aggregate.body;
-						type->aggInst.params = maybeClone( o->type->aggregate.actuals );
+						type->aggInst.params = maybeCopy( o->type->aggregate.actuals );
 					} else {
 						type->aggInst.hoistType = o->type->enumeration.body;
@@ -860,5 +858,5 @@
 				p->type->base->aggInst.aggregate = type;
 				if ( type->kind == TypeData::Aggregate ) {
-					p->type->base->aggInst.params = maybeClone( type->aggregate.actuals );
+					p->type->base->aggInst.params = maybeCopy( type->aggregate.actuals );
 				} // if
 				p->type->base->qualifiers |= type->qualifiers;
@@ -897,5 +895,5 @@
 			lastArray->base->aggInst.aggregate = type;
 			if ( type->kind == TypeData::Aggregate ) {
-				lastArray->base->aggInst.params = maybeClone( type->aggregate.actuals );
+				lastArray->base->aggInst.params = maybeCopy( type->aggregate.actuals );
 			} // if
 			lastArray->base->qualifiers |= type->qualifiers;
@@ -950,5 +948,5 @@
 DeclarationNode * DeclarationNode::cloneType( string * name ) {
 	DeclarationNode * newnode = newName( name );
-	newnode->type = maybeClone( type );
+	newnode->type = maybeCopy( type );
 	newnode->copySpecifiers( this );
 	return newnode;
@@ -984,5 +982,5 @@
 		} // if
 
-		newType->forall = maybeClone( type->forall );
+		newType->forall = maybeCopy( type->forall );
 		if ( ! o->type ) {
 			o->type = newType;
Index: src/Parser/ParseNode.h
===================================================================
--- src/Parser/ParseNode.h	(revision 59c8dff49bbb5284b39ab27da894a644327a531a)
+++ src/Parser/ParseNode.h	(revision f988834af650698a46072a2a91bb190224a866bd)
@@ -30,5 +30,4 @@
 #include "Common/SemanticError.h"  // for SemanticError
 #include "Common/UniqueName.h"     // for UniqueName
-#include "Common/utility.h"        // for maybeClone
 #include "Parser/parserutility.h"  // for maybeBuild, maybeCopy
 
Index: src/Parser/TypeData.cc
===================================================================
--- src/Parser/TypeData.cc	(revision 59c8dff49bbb5284b39ab27da894a644327a531a)
+++ src/Parser/TypeData.cc	(revision f988834af650698a46072a2a91bb190224a866bd)
@@ -167,6 +167,6 @@
 	TypeData * newtype = new TypeData( kind );
 	newtype->qualifiers = qualifiers;
-	newtype->base = maybeClone( base );
-	newtype->forall = maybeClone( forall );
+	newtype->base = maybeCopy( base );
+	newtype->forall = maybeCopy( forall );
 
 	switch ( kind ) {
@@ -185,21 +185,21 @@
 		break;
 	case Array:
-		newtype->array.dimension = maybeClone( array.dimension );
+		newtype->array.dimension = maybeCopy( array.dimension );
 		newtype->array.isVarLen = array.isVarLen;
 		newtype->array.isStatic = array.isStatic;
 		break;
 	case Function:
-		newtype->function.params = maybeClone( function.params );
-		newtype->function.idList = maybeClone( function.idList );
-		newtype->function.oldDeclList = maybeClone( function.oldDeclList );
-		newtype->function.body = maybeClone( function.body );
-		newtype->function.withExprs = maybeClone( function.withExprs );
+		newtype->function.params = maybeCopy( function.params );
+		newtype->function.idList = maybeCopy( function.idList );
+		newtype->function.oldDeclList = maybeCopy( function.oldDeclList );
+		newtype->function.body = maybeCopy( function.body );
+		newtype->function.withExprs = maybeCopy( function.withExprs );
 		break;
 	case Aggregate:
 		newtype->aggregate.kind = aggregate.kind;
 		newtype->aggregate.name = aggregate.name ? new string( *aggregate.name ) : nullptr;
-		newtype->aggregate.params = maybeClone( aggregate.params );
-		newtype->aggregate.actuals = maybeClone( aggregate.actuals );
-		newtype->aggregate.fields = maybeClone( aggregate.fields );
+		newtype->aggregate.params = maybeCopy( aggregate.params );
+		newtype->aggregate.actuals = maybeCopy( aggregate.actuals );
+		newtype->aggregate.fields = maybeCopy( aggregate.fields );
 		newtype->aggregate.body = aggregate.body;
 		newtype->aggregate.anon = aggregate.anon;
@@ -208,11 +208,11 @@
 		break;
 	case AggregateInst:
-		newtype->aggInst.aggregate = maybeClone( aggInst.aggregate );
-		newtype->aggInst.params = maybeClone( aggInst.params );
+		newtype->aggInst.aggregate = maybeCopy( aggInst.aggregate );
+		newtype->aggInst.params = maybeCopy( aggInst.params );
 		newtype->aggInst.hoistType = aggInst.hoistType;
 		break;
 	case Enum:
 		newtype->enumeration.name = enumeration.name ? new string( *enumeration.name ) : nullptr;
-		newtype->enumeration.constants = maybeClone( enumeration.constants );
+		newtype->enumeration.constants = maybeCopy( enumeration.constants );
 		newtype->enumeration.body = enumeration.body;
 		newtype->enumeration.anon = enumeration.anon;
@@ -221,15 +221,15 @@
 	case SymbolicInst:
 		newtype->symbolic.name = symbolic.name ? new string( *symbolic.name ) : nullptr;
-		newtype->symbolic.params = maybeClone( symbolic.params );
-		newtype->symbolic.actuals = maybeClone( symbolic.actuals );
-		newtype->symbolic.assertions = maybeClone( symbolic.assertions );
+		newtype->symbolic.params = maybeCopy( symbolic.params );
+		newtype->symbolic.actuals = maybeCopy( symbolic.actuals );
+		newtype->symbolic.assertions = maybeCopy( symbolic.assertions );
 		newtype->symbolic.isTypedef = symbolic.isTypedef;
 		break;
 	case Tuple:
-		newtype->tuple = maybeClone( tuple );
+		newtype->tuple = maybeCopy( tuple );
 		break;
 	case Typeof:
 	case Basetypeof:
-		newtype->typeexpr = maybeClone( typeexpr );
+		newtype->typeexpr = maybeCopy( typeexpr );
 		break;
 	case Vtable:
@@ -240,6 +240,6 @@
 		break;
 	case Qualified:
-		newtype->qualified.parent = maybeClone( qualified.parent );
-		newtype->qualified.child = maybeClone( qualified.child );
+		newtype->qualified.parent = maybeCopy( qualified.parent );
+		newtype->qualified.child = maybeCopy( qualified.child );
 		break;
 	} // switch
Index: src/Parser/parser.yy
===================================================================
--- src/Parser/parser.yy	(revision 59c8dff49bbb5284b39ab27da894a644327a531a)
+++ src/Parser/parser.yy	(revision f988834af650698a46072a2a91bb190224a866bd)
@@ -1960,5 +1960,5 @@
 			// Append the return type at the start (left-hand-side) to each identifier in the list.
 			DeclarationNode * ret = new DeclarationNode;
-			ret->type = maybeClone( $1->type->base );
+			ret->type = maybeCopy( $1->type->base );
 			$$ = $1->appendList( DeclarationNode::newFunction( $3, ret, $6, nullptr ) );
 		}
Index: src/Parser/parserutility.h
===================================================================
--- src/Parser/parserutility.h	(revision 59c8dff49bbb5284b39ab27da894a644327a531a)
+++ src/Parser/parserutility.h	(revision f988834af650698a46072a2a91bb190224a866bd)
@@ -36,5 +36,5 @@
 
 template<typename node_t>
-node_t * maybeCopy( node_t const * node ) {
+static inline node_t * maybeCopy( node_t const * node ) {
 	return node ? ast::shallowCopy( node ) : nullptr;
 }
Index: src/SymTab/GenImplicitCall.cpp
===================================================================
--- src/SymTab/GenImplicitCall.cpp	(revision 59c8dff49bbb5284b39ab27da894a644327a531a)
+++ src/SymTab/GenImplicitCall.cpp	(revision f988834af650698a46072a2a91bb190224a866bd)
@@ -25,4 +25,5 @@
 #include "CodeGen/OperatorTable.h"       // for isCtorDtor
 #include "Common/UniqueName.h"           // for UniqueName
+#include "Common/utility.h"              // for splice
 
 namespace SymTab {
Index: src/Validate/Autogen.cpp
===================================================================
--- src/Validate/Autogen.cpp	(revision 59c8dff49bbb5284b39ab27da894a644327a531a)
+++ src/Validate/Autogen.cpp	(revision f988834af650698a46072a2a91bb190224a866bd)
@@ -445,5 +445,7 @@
 
 		auto * paramType = ast::deepCopy( member->get_type() );
-		paramType->attributes.clear();
+		erase_if( paramType->attributes, []( ast::Attribute const * attr ){
+			return !attr->isValidOnFuncParam();
+		} );
 		ast::ObjectDecl * param = new ast::ObjectDecl(
 			getLocation(), member->name, paramType );
Index: src/Validate/ReplaceTypedef.cpp
===================================================================
--- src/Validate/ReplaceTypedef.cpp	(revision 59c8dff49bbb5284b39ab27da894a644327a531a)
+++ src/Validate/ReplaceTypedef.cpp	(revision f988834af650698a46072a2a91bb190224a866bd)
@@ -25,16 +25,4 @@
 
 namespace {
-
-bool isNonParameterAttribute( ast::Attribute const * attr ) {
-	static const std::vector<std::string> bad_names = {
-		"aligned", "__aligned__",
-	};
-	for ( auto name : bad_names ) {
-		if ( name == attr->name ) {
-			return true;
-		}
-	}
-	return false;
-}
 
 struct ReplaceTypedefCore final :
@@ -101,5 +89,7 @@
 		// by typedef. GCC appears to do the same thing.
 		if ( isAtFunctionTop ) {
-			erase_if( ret->attributes, isNonParameterAttribute );
+			erase_if( ret->attributes, []( ast::Attribute const * attr ){
+				return !attr->isValidOnFuncParam();
+			} );
 		}
 		for ( const auto & attribute : type->attributes ) {
