Index: src/SymTab/Autogen.cc
===================================================================
--- src/SymTab/Autogen.cc	(revision 0b0f1dd5a6f46d23ab33e2091192a9f415f7ad92)
+++ src/SymTab/Autogen.cc	(revision 6c7c63cec71bc05334536c6b43fd8ddca9d0a39c)
@@ -377,5 +377,7 @@
 			paramType->attributes.clear();
 			// add a parameter corresponding to this field
-			memCtorType->parameters.push_back( new ObjectDecl( field->name, Type::StorageClasses(), LinkageSpec::Cforall, nullptr, paramType, nullptr ) );
+			ObjectDecl * param = new ObjectDecl( field->name, Type::StorageClasses(), LinkageSpec::Cforall, nullptr, paramType, nullptr );
+			cloneAll_if( field->attributes, param->attributes, [](Attribute * attr) { return attr->isValidOnFuncParam(); } );
+			memCtorType->parameters.push_back( param );
 			FunctionDecl * ctor = genFunc( "?{}", memCtorType->clone(), functionNesting );
 			makeFieldCtorBody( aggregateDecl->members.begin(), aggregateDecl->members.end(), ctor );
Index: src/SymTab/FixFunction.cc
===================================================================
--- src/SymTab/FixFunction.cc	(revision 0b0f1dd5a6f46d23ab33e2091192a9f415f7ad92)
+++ src/SymTab/FixFunction.cc	(revision 6c7c63cec71bc05334536c6b43fd8ddca9d0a39c)
@@ -36,4 +36,8 @@
 	}
 
+	// xxx - this passes on void[], e.g.
+	//   void foo(void [10]);
+	// does not cause an error
+
 	Type * FixFunction::postmutate(ArrayType *arrayType) {
 		// need to recursively mutate the base type in order for multi-dimensional arrays to work.
@@ -62,4 +66,10 @@
 	void FixFunction::premutate(ZeroType *) { visit_children = false; }
 	void FixFunction::premutate(OneType *) { visit_children = false; }
+
+	bool fixFunction( DeclarationWithType *& dwt ) {
+		PassVisitor<FixFunction> fixer;
+		dwt = dwt->acceptMutator( fixer );
+		return fixer.pass.isVoid;
+	}
 } // namespace SymTab
 
Index: src/SymTab/FixFunction.h
===================================================================
--- src/SymTab/FixFunction.h	(revision 0b0f1dd5a6f46d23ab33e2091192a9f415f7ad92)
+++ src/SymTab/FixFunction.h	(revision 6c7c63cec71bc05334536c6b43fd8ddca9d0a39c)
@@ -47,4 +47,6 @@
 		bool isVoid;
 	};
+
+	bool fixFunction( DeclarationWithType *& );
 } // namespace SymTab
 
Index: src/SymTab/Indexer.cc
===================================================================
--- src/SymTab/Indexer.cc	(revision 0b0f1dd5a6f46d23ab33e2091192a9f415f7ad92)
+++ src/SymTab/Indexer.cc	(revision 6c7c63cec71bc05334536c6b43fd8ddca9d0a39c)
@@ -286,7 +286,7 @@
 	}
 
-	DeclarationWithType *Indexer::lookupIdAtScope( const std::string &id, const std::string &mangleName, unsigned long scope ) const {
-		if ( ! tables ) return 0;
-		if ( tables->scope < scope ) return 0;
+	const Indexer::IdData * Indexer::lookupIdAtScope( const std::string &id, const std::string &mangleName, unsigned long scope ) const {
+		if ( ! tables ) return nullptr;
+		if ( tables->scope < scope ) return nullptr;
 
 		IdTable::const_iterator decls = tables->idTable.find( id );
@@ -294,8 +294,12 @@
 			const MangleTable &mangleTable = decls->second;
 			MangleTable::const_iterator decl = mangleTable.find( mangleName );
-			if ( decl != mangleTable.end() ) return decl->second.id;
+			if ( decl != mangleTable.end() ) return &decl->second;
 		}
 
 		return tables->base.lookupIdAtScope( id, mangleName, scope );
+	}
+
+	Indexer::IdData * Indexer::lookupIdAtScope( const std::string &id, const std::string &mangleName, unsigned long scope ) {
+		return const_cast<IdData *>(const_cast<const Indexer *>(this)->lookupIdAtScope( id, mangleName, scope ));
 	}
 
@@ -373,20 +377,28 @@
 	}
 
-	bool addedIdConflicts( DeclarationWithType *existing, DeclarationWithType *added ) {
+	bool addedIdConflicts( Indexer::IdData & existing, DeclarationWithType *added, BaseSyntaxNode * deleteStmt, Indexer::ConflictFunction handleConflicts ) {
 		// if we're giving the same name mangling to things of different types then there is something wrong
-		assert( (dynamic_cast<ObjectDecl*>( added ) && dynamic_cast<ObjectDecl*>( existing ) )
-			|| (dynamic_cast<FunctionDecl*>( added ) && dynamic_cast<FunctionDecl*>( existing ) ) );
-
-		if ( LinkageSpec::isOverridable( existing->get_linkage() ) ) {
+		assert( (dynamic_cast<ObjectDecl*>( added ) && dynamic_cast<ObjectDecl*>( existing.id ) )
+			|| (dynamic_cast<FunctionDecl*>( added ) && dynamic_cast<FunctionDecl*>( existing.id ) ) );
+
+		if ( LinkageSpec::isOverridable( existing.id->get_linkage() ) ) {
 			// new definition shadows the autogenerated one, even at the same scope
 			return false;
-		} else if ( LinkageSpec::isMangled( added->get_linkage() ) || ResolvExpr::typesCompatible( added->get_type(), existing->get_type(), Indexer() ) ) {
+		} else if ( LinkageSpec::isMangled( added->get_linkage() ) || ResolvExpr::typesCompatible( added->get_type(), existing.id->get_type(), Indexer() ) ) {
+
+			// it is a conflict if one declaration is deleted and the other is not
+			if ( deleteStmt && ! existing.deleteStmt ) {
+				return handleConflicts( existing, "deletion of defined identifier " );
+			} else if ( ! deleteStmt && existing.deleteStmt ) {
+				return handleConflicts( existing, "definition of deleted identifier " );
+			}
+
 			// typesCompatible doesn't really do the right thing here. When checking compatibility of function types,
 			// we should ignore outermost pointer qualifiers, except _Atomic?
-			FunctionDecl *newentry = dynamic_cast< FunctionDecl* >( added );
-			FunctionDecl *oldentry = dynamic_cast< FunctionDecl* >( existing );
+			FunctionDecl * newentry = dynamic_cast< FunctionDecl * >( added );
+			FunctionDecl * oldentry = dynamic_cast< FunctionDecl * >( existing.id );
 			if ( newentry && oldentry ) {
 				if ( newentry->get_statements() && oldentry->get_statements() ) {
-					throw SemanticError( added, "duplicate function definition for " );
+					return handleConflicts( existing, "duplicate function definition for " );
 				} // if
 			} else {
@@ -395,12 +407,12 @@
 				// xxx - perhaps it's actually if either is intrinsic then this is okay?
 				//       might also need to be same storage class?
-				ObjectDecl *newobj = dynamic_cast< ObjectDecl* >( added );
-				ObjectDecl *oldobj = dynamic_cast< ObjectDecl* >( existing );
+				ObjectDecl * newobj = dynamic_cast< ObjectDecl * >( added );
+				ObjectDecl * oldobj = dynamic_cast< ObjectDecl * >( existing.id );
 				if ( ! newobj->get_storageClasses().is_extern && ! oldobj->get_storageClasses().is_extern ) {
-					throw SemanticError( added, "duplicate object definition for " );
+					return handleConflicts( existing, "duplicate object definition for " );
 				} // if
 			} // if
 		} else {
-			throw SemanticError( added, "duplicate definition for " );
+			return handleConflicts( existing, "duplicate definition for " );
 		} // if
 
@@ -408,5 +420,5 @@
 	}
 
-	void Indexer::addId( DeclarationWithType *decl, Expression * baseExpr ) {
+	void Indexer::addId( DeclarationWithType *decl, ConflictFunction handleConflicts, Expression * baseExpr, BaseSyntaxNode * deleteStmt ) {
 		if ( decl->name == "" ) return;
 		debugPrint( "Adding Id " << decl->name << std::endl );
@@ -434,5 +446,5 @@
 			}
 		} else {
-			// Check that a Cforall declaration doesn't overload any C declaration
+			// Check that a Cforall declaration doesn't override any C declaration
 			if ( hasCompatibleCDecl( name, mangleName, scope ) ) {
 				throw SemanticError( decl, "Cforall declaration hides C function " );
@@ -441,10 +453,20 @@
 
 		// Skip repeat declarations of the same identifier
-		DeclarationWithType *existing = lookupIdAtScope( name, mangleName, scope );
-		if ( existing && addedIdConflicts( existing, decl ) ) return;
+		IdData * existing = lookupIdAtScope( name, mangleName, scope );
+		if ( existing && existing->id && addedIdConflicts( *existing, decl, deleteStmt, handleConflicts ) ) return;
 
 		// add to indexer
-		tables->idTable[ name ][ mangleName ] = { decl, baseExpr };
+		tables->idTable[ name ][ mangleName ] = { decl, baseExpr, deleteStmt };
 		++tables->size;
+	}
+
+	void Indexer::addId( DeclarationWithType * decl, Expression * baseExpr ) {
+		// default handling of conflicts is to raise an error
+		addId( decl, [decl](IdData &, const std::string & msg) { throw SemanticError( decl, msg ); return true; }, baseExpr );
+	}
+
+	void Indexer::addDeletedId( DeclarationWithType * decl, BaseSyntaxNode * deleteStmt ) {
+		// default handling of conflicts is to raise an error
+		addId( decl, [decl](IdData &, const std::string & msg) { throw SemanticError( decl, msg ); return true; }, nullptr, deleteStmt );
 	}
 
@@ -573,8 +595,8 @@
 	}
 
-	void Indexer::addMembers( AggregateDecl * aggr, Expression * expr ) {
+	void Indexer::addMembers( AggregateDecl * aggr, Expression * expr, ConflictFunction handleConflicts ) {
 		for ( Declaration * decl : aggr->members ) {
 			if ( DeclarationWithType * dwt = dynamic_cast< DeclarationWithType * >( decl ) ) {
-				addId( dwt, expr );
+				addId( dwt, handleConflicts, expr );
 				if ( dwt->name == "" ) {
 					Type * t = dwt->get_type()->stripReferences();
@@ -582,5 +604,5 @@
 						Expression * base = expr->clone();
 						ResolvExpr::referenceToRvalueConversion( base );
-						addMembers( t->getAggr(), new MemberExpr( dwt, base ) );
+						addMembers( t->getAggr(), new MemberExpr( dwt, base ), handleConflicts );
 					}
 				}
@@ -589,5 +611,5 @@
 	}
 
-	void Indexer::addWith( std::list< Expression * > & withExprs ) {
+	void Indexer::addWith( std::list< Expression * > & withExprs, BaseSyntaxNode * withStmt ) {
 		for ( Expression * expr : withExprs ) {
 			if ( expr->result ) {
@@ -595,5 +617,9 @@
 				assertf( aggr, "WithStmt expr has non-aggregate type: %s", toString( expr->result ).c_str() );
 
-				addMembers( aggr, expr );
+				addMembers( aggr, expr, [withStmt](IdData & existing, const std::string &) {
+					// on conflict, delete the identifier
+					existing.deleteStmt = withStmt;
+					return true;
+				});
 			}
 		}
@@ -654,5 +680,5 @@
 
 	void Indexer::print( std::ostream &os, int indent ) const {
-	    using std::cerr;
+		using std::cerr;
 
 		if ( tables ) {
@@ -680,16 +706,18 @@
 
 	Expression * Indexer::IdData::combine() const {
+		Expression * ret = nullptr;
 		if ( baseExpr ) {
 			Expression * base = baseExpr->clone();
 			ResolvExpr::referenceToRvalueConversion( base );
-			Expression * ret = new MemberExpr( id, base );
+			ret = new MemberExpr( id, base );
 			// xxx - this introduces hidden environments, for now remove them.
 			// std::swap( base->env, ret->env );
 			delete base->env;
 			base->env = nullptr;
-			return ret;
-		} else {
-			return new VariableExpr( id );
-		}
+		} else {
+			ret = new VariableExpr( id );
+		}
+		if ( deleteStmt ) ret = new DeletedExpr( ret, deleteStmt );
+		return ret;
 	}
 } // namespace SymTab
Index: src/SymTab/Indexer.h
===================================================================
--- src/SymTab/Indexer.h	(revision 0b0f1dd5a6f46d23ab33e2091192a9f415f7ad92)
+++ src/SymTab/Indexer.h	(revision 6c7c63cec71bc05334536c6b43fd8ddca9d0a39c)
@@ -19,4 +19,5 @@
 #include <list>               // for list
 #include <string>             // for string
+#include <functional>         // for function
 
 #include "SynTree/Visitor.h"  // for Visitor
@@ -40,6 +41,9 @@
 
 		struct IdData {
-			DeclarationWithType * id;
-			Expression * baseExpr; // WithExpr
+			DeclarationWithType * id = nullptr;
+			Expression * baseExpr = nullptr; // WithExpr
+
+			/// non-null if this declaration is deleted
+			BaseSyntaxNode * deleteStmt = nullptr;
 
 			Expression * combine() const;
@@ -62,5 +66,6 @@
 
 		/// looks up a specific mangled ID at the given scope
-		DeclarationWithType *lookupIdAtScope( const std::string &id, const std::string &mangleName, unsigned long scope ) const;
+		IdData * lookupIdAtScope( const std::string &id, const std::string &mangleName, unsigned long scope );
+		const IdData * lookupIdAtScope( const std::string &id, const std::string &mangleName, unsigned long scope ) const;
 		/// returns true if there exists a declaration with C linkage and the given name with a different mangled name
 		bool hasIncompatibleCDecl( const std::string &id, const std::string &mangleName, unsigned long scope ) const;
@@ -74,5 +79,9 @@
 		TraitDecl *lookupTraitAtScope( const std::string &id, unsigned long scope ) const;
 
-		void addId( DeclarationWithType *decl, Expression * baseExpr = nullptr );
+		typedef std::function<bool(IdData &, const std::string &)> ConflictFunction;
+
+		void addId( DeclarationWithType * decl, Expression * baseExpr = nullptr );
+		void addDeletedId( DeclarationWithType * decl, BaseSyntaxNode * deleteStmt );
+
 		void addType( NamedTypeDecl *decl );
 		void addStruct( const std::string &id );
@@ -84,8 +93,8 @@
 
 		/// adds all of the IDs from WithStmt exprs
-		void addWith( std::list< Expression * > & withExprs );
+		void addWith( std::list< Expression * > & withExprs, BaseSyntaxNode * withStmt );
 
 		/// adds all of the members of the Aggregate (addWith helper)
-		void addMembers( AggregateDecl * aggr, Expression * expr );
+		void addMembers( AggregateDecl * aggr, Expression * expr, ConflictFunction );
 
 		/// convenience function for adding a list of Ids to the indexer
@@ -117,4 +126,7 @@
 		/// Ensures that tables variable is writable (i.e. allocated, uniquely owned by this Indexer, and at the current scope)
 		void makeWritable();
+
+		/// common code for addId, addDeletedId, etc.
+		void addId( DeclarationWithType * decl, ConflictFunction, Expression * baseExpr = nullptr, BaseSyntaxNode * deleteStmt = nullptr );
 	};
 } // namespace SymTab
Index: src/SymTab/Mangler.cc
===================================================================
--- src/SymTab/Mangler.cc	(revision 0b0f1dd5a6f46d23ab33e2091192a9f415f7ad92)
+++ src/SymTab/Mangler.cc	(revision 6c7c63cec71bc05334536c6b43fd8ddca9d0a39c)
@@ -37,5 +37,5 @@
 			struct Mangler : public WithShortCircuiting, public WithVisitorRef<Mangler> {
 				Mangler( bool mangleOverridable, bool typeMode, bool mangleGenericParams );
-				Mangler( const Mangler & );
+				Mangler( const Mangler & ) = delete;
 
 				void previsit( BaseSyntaxNode * ) { visit_children = false; }
Index: src/SymTab/Validate.cc
===================================================================
--- src/SymTab/Validate.cc	(revision 0b0f1dd5a6f46d23ab33e2091192a9f415f7ad92)
+++ src/SymTab/Validate.cc	(revision 6c7c63cec71bc05334536c6b43fd8ddca9d0a39c)
@@ -351,31 +351,22 @@
 	namespace {
 		template< typename DWTList >
-		void fixFunctionList( DWTList & dwts, FunctionType * func ) {
-			// the only case in which "void" is valid is where it is the only one in the list; then it should be removed
-			// entirely. other fix ups are handled by the FixFunction class
-			typedef typename DWTList::iterator DWTIterator;
-			DWTIterator begin( dwts.begin() ), end( dwts.end() );
-			if ( begin == end ) return;
-			PassVisitor<FixFunction> fixer;
-			DWTIterator i = begin;
-			*i = (*i)->acceptMutator( fixer );
-			if ( fixer.pass.isVoid ) {
-				DWTIterator j = i;
-				++i;
-				delete *j;
-				dwts.erase( j );
-				if ( i != end ) {
-					throw SemanticError( func, "invalid type void in function type " );
-				} // if
-			} else {
-				++i;
-				for ( ; i != end; ++i ) {
-					PassVisitor<FixFunction> fixer;
-					*i = (*i)->acceptMutator( fixer );
-					if ( fixer.pass.isVoid ) {
-						throw SemanticError( func, "invalid type void in function type " );
-					} // if
-				} // for
-			} // if
+		void fixFunctionList( DWTList & dwts, bool isVarArgs, FunctionType * func ) {
+			auto nvals = dwts.size();
+			bool containsVoid = false;
+			for ( auto & dwt : dwts ) {
+				// fix each DWT and record whether a void was found
+				containsVoid |= fixFunction( dwt );
+			}
+
+			// the only case in which "void" is valid is where it is the only one in the list
+			if ( containsVoid && ( nvals > 1 || isVarArgs ) ) {
+				throw SemanticError( func, "invalid type void in function type " );
+			}
+
+			// one void is the only thing in the list; remove it.
+			if ( containsVoid ) {
+				delete dwts.front();
+				dwts.clear();
+			}
 		}
 	}
@@ -383,6 +374,6 @@
 	void EnumAndPointerDecay::previsit( FunctionType *func ) {
 		// Fix up parameters and return types
-		fixFunctionList( func->get_parameters(), func );
-		fixFunctionList( func->get_returnVals(), func );
+		fixFunctionList( func->parameters, func->isVarArgs, func );
+		fixFunctionList( func->returnVals, false, func );
 	}
 
@@ -626,7 +617,6 @@
 			// apply FixFunction to every assertion to check for invalid void type
 			for ( DeclarationWithType *& assertion : type->assertions ) {
-				PassVisitor<FixFunction> fixer;
-				assertion = assertion->acceptMutator( fixer );
-				if ( fixer.pass.isVoid ) {
+				bool isVoid = fixFunction( assertion );
+				if ( isVoid ) {
 					throw SemanticError( node, "invalid type void in assertion of function " );
 				} // if
