Index: src/Common/PassVisitor.h
===================================================================
--- src/Common/PassVisitor.h	(revision f5883bd645373ae435095db065ad1bb87a8e8c9b)
+++ src/Common/PassVisitor.h	(revision 0ac366bb815a57d58e603b8e1ef744766f2255e4)
@@ -305,5 +305,5 @@
 	void indexerAddUnionFwd ( UnionDecl                 * node  ) { indexer_impl_addUnionFwd ( pass, 0, node ); }
 	void indexerAddTrait    ( TraitDecl                 * node  ) { indexer_impl_addTrait    ( pass, 0, node ); }
-	void indexerAddWith     ( std::list< Expression * > & exprs ) { indexer_impl_addWith     ( pass, 0, exprs ); }
+	void indexerAddWith     ( std::list< Expression * > & exprs, BaseSyntaxNode * withStmt ) { indexer_impl_addWith     ( pass, 0, exprs, withStmt ); }
 
 
Index: src/Common/PassVisitor.impl.h
===================================================================
--- src/Common/PassVisitor.impl.h	(revision f5883bd645373ae435095db065ad1bb87a8e8c9b)
+++ src/Common/PassVisitor.impl.h	(revision 0ac366bb815a57d58e603b8e1ef744766f2255e4)
@@ -398,5 +398,5 @@
 		// shadow with exprs and not the other way around.
 		auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
-		indexerAddWith( node->withExprs );
+		indexerAddWith( node->withExprs, node );
 		{
 			auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
@@ -428,5 +428,5 @@
 		// shadow with exprs and not the other way around.
 		auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
-		indexerAddWith( node->withExprs );
+		indexerAddWith( node->withExprs, node );
 		{
 			auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
@@ -1069,5 +1069,5 @@
 		// catch statements introduce a level of scope (for the caught exception)
 		auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
-		indexerAddWith( node->exprs );
+		indexerAddWith( node->exprs, node );
 		maybeAccept_impl( node->stmt, *this );
 	}
@@ -1082,5 +1082,5 @@
 		// catch statements introduce a level of scope (for the caught exception)
 		auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
-		indexerAddWith( node->exprs );
+		indexerAddWith( node->exprs, node );
 		maybeMutate_impl( node->stmt, *this );
 	}
Index: src/Common/PassVisitor.proto.h
===================================================================
--- src/Common/PassVisitor.proto.h	(revision f5883bd645373ae435095db065ad1bb87a8e8c9b)
+++ src/Common/PassVisitor.proto.h	(revision 0ac366bb815a57d58e603b8e1ef744766f2255e4)
@@ -193,5 +193,5 @@
 
 
-#define INDEXER_FUNC( func, type )                                                                                             \
+#define INDEXER_FUNC1( func, type )                                                                                             \
 template<typename pass_type>                                                                                                   \
 static inline auto indexer_impl_##func ( pass_type & pass, int, type arg ) -> decltype( pass.indexer.func( arg ), void() ) {   \
@@ -202,11 +202,21 @@
 static inline void indexer_impl_##func ( pass_type &, long, type ) { }                                                          \
 
-INDEXER_FUNC( addId     , DeclarationWithType *       );
-INDEXER_FUNC( addType   , NamedTypeDecl *             );
-INDEXER_FUNC( addStruct , StructDecl *                );
-INDEXER_FUNC( addEnum   , EnumDecl *                  );
-INDEXER_FUNC( addUnion  , UnionDecl *                 );
-INDEXER_FUNC( addTrait  , TraitDecl *                 );
-INDEXER_FUNC( addWith   , std::list< Expression * > & );
+#define INDEXER_FUNC2( func, type1, type2 )                                                                                             \
+template<typename pass_type>                                                                                                   \
+static inline auto indexer_impl_##func ( pass_type & pass, int, type1 arg1, type2 arg2 ) -> decltype( pass.indexer.func( arg1, arg2 ), void() ) {   \
+	pass.indexer.func( arg1, arg2 );                                                                                                \
+}                                                                                                                              \
+                                                                                                                               \
+template<typename pass_type>                                                                                                   \
+static inline void indexer_impl_##func ( pass_type &, long, type1, type2 ) { }
+
+
+INDEXER_FUNC1( addId     , DeclarationWithType *       );
+INDEXER_FUNC1( addType   , NamedTypeDecl *             );
+INDEXER_FUNC1( addStruct , StructDecl *                );
+INDEXER_FUNC1( addEnum   , EnumDecl *                  );
+INDEXER_FUNC1( addUnion  , UnionDecl *                 );
+INDEXER_FUNC1( addTrait  , TraitDecl *                 );
+INDEXER_FUNC2( addWith   , std::list< Expression * > &, BaseSyntaxNode * );
 
 
Index: src/SymTab/Indexer.cc
===================================================================
--- src/SymTab/Indexer.cc	(revision f5883bd645373ae435095db065ad1bb87a8e8c9b)
+++ src/SymTab/Indexer.cc	(revision 0ac366bb815a57d58e603b8e1ef744766f2255e4)
@@ -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( "duplicate function definition for ", added );
+					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( "duplicate object definition for ", added );
+					return handleConflicts( existing, "duplicate object definition for " );
 				} // if
 			} // if
 		} else {
-			throw SemanticError( "duplicate definition for ", added );
+			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 );
@@ -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( msg, decl ); 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( msg, decl ); 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;
+				});
 			}
 		}
@@ -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 f5883bd645373ae435095db065ad1bb87a8e8c9b)
+++ src/SymTab/Indexer.h	(revision 0ac366bb815a57d58e603b8e1ef744766f2255e4)
@@ -19,4 +19,5 @@
 #include <list>               // for list
 #include <string>             // for string
+#include <functional>         // for function
 
 #include "SynTree/Visitor.h"  // for Visitor
@@ -65,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;
@@ -77,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 );
@@ -87,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
@@ -120,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
