Index: src/AST/Pass.hpp
===================================================================
--- src/AST/Pass.hpp	(revision 6a77224bf06bbc28a67a04ce5efe7106814a9045)
+++ src/AST/Pass.hpp	(revision 4ae78c10a7d9e92dd079b2693e1de5ac44e5f368)
@@ -296,4 +296,5 @@
 private:
 	bool inFunction = false;
+	bool atFunctionTop = false;
 };
 
Index: src/AST/Pass.impl.hpp
===================================================================
--- src/AST/Pass.impl.hpp	(revision 6a77224bf06bbc28a67a04ce5efe7106814a9045)
+++ src/AST/Pass.impl.hpp	(revision 4ae78c10a7d9e92dd079b2693e1de5ac44e5f368)
@@ -502,8 +502,11 @@
 				// 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
-				// a new scope if inFunction is true
+				// First remember that we are now within a function.
 				ValueGuard< bool > oldInFunction( inFunction );
 				inFunction = true;
+				// The function body needs to have the same scope as parameters.
+				// A CompoundStmt will not enter a new scope if atFunctionTop is true.
+				ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
+				atFunctionTop = true;
 				maybe_accept( node, &FunctionDecl::stmts );
 				maybe_accept( node, &FunctionDecl::attributes );
@@ -672,9 +675,9 @@
 	VISIT_START( node );
 	VISIT({
-		// do not enter a new scope if inFunction is true - needs to check old state before the assignment
-		auto guard1 = makeFuncGuard( [this, inFunctionCpy = this->inFunction]() {
-			if ( ! inFunctionCpy ) __pass::symtab::enter(core, 0);
-		}, [this, inFunctionCpy = this->inFunction]() {
-			if ( ! inFunctionCpy ) __pass::symtab::leave(core, 0);
+		// Do not enter (or leave) a new scope if atFunctionTop. Remember to save the result.
+		auto guard1 = makeFuncGuard( [this, enterScope = !this->atFunctionTop]() {
+			if ( enterScope ) __pass::symtab::enter(core, 0);
+		}, [this, leaveScope = !this->atFunctionTop]() {
+			if ( leaveScope ) __pass::symtab::leave(core, 0);
 		});
 		ValueGuard< bool > guard2( inFunction );
Index: src/Common/PassVisitor.h
===================================================================
--- src/Common/PassVisitor.h	(revision 6a77224bf06bbc28a67a04ce5efe7106814a9045)
+++ src/Common/PassVisitor.h	(revision 4ae78c10a7d9e92dd079b2693e1de5ac44e5f368)
@@ -360,4 +360,5 @@
 private:
 	bool inFunction = false;
+	bool atFunctionTop = false;
 
 	template<typename pass_t> friend void acceptAll( std::list< Declaration* > &decls, PassVisitor< pass_t >& visitor );
@@ -532,5 +533,5 @@
 
 	bool isInFunction() const {
-		return visitor->inFunction;
+		return visitor->isInFunction();
 	}
 };
Index: src/Common/PassVisitor.impl.h
===================================================================
--- src/Common/PassVisitor.impl.h	(revision 6a77224bf06bbc28a67a04ce5efe7106814a9045)
+++ src/Common/PassVisitor.impl.h	(revision 4ae78c10a7d9e92dd079b2693e1de5ac44e5f368)
@@ -532,8 +532,11 @@
 			indexerAddId( &func );
 			maybeAccept_impl( node->type, *this );
-			// function body needs to have the same scope as parameters - CompoundStmt will not enter
-			// a new scope if inFunction is true
+			// First remember that we are now within a function.
 			ValueGuard< bool > oldInFunction( inFunction );
 			inFunction = true;
+			// The function body needs to have the same scope as parameters.
+			// A CompoundStmt will not enter a new scope if atFunctionTop is true.
+			ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
+			atFunctionTop = true;
 			maybeAccept_impl( node->statements, *this );
 			maybeAccept_impl( node->attributes, *this );
@@ -567,8 +570,11 @@
 			indexerAddId( &func );
 			maybeAccept_impl( node->type, *this );
-			// function body needs to have the same scope as parameters - CompoundStmt will not enter
-			// a new scope if inFunction is true
+			// First remember that we are now within a function.
 			ValueGuard< bool > oldInFunction( inFunction );
 			inFunction = true;
+			// The function body needs to have the same scope as parameters.
+			// A CompoundStmt will not enter a new scope if atFunctionTop is true.
+			ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
+			atFunctionTop = true;
 			maybeAccept_impl( node->statements, *this );
 			maybeAccept_impl( node->attributes, *this );
@@ -601,8 +607,11 @@
 			indexerAddId( &func );
 			maybeMutate_impl( node->type, *this );
-			// function body needs to have the same scope as parameters - CompoundStmt will not enter
-			// a new scope if inFunction is true
+			// First remember that we are now within a function.
 			ValueGuard< bool > oldInFunction( inFunction );
 			inFunction = true;
+			// The function body needs to have the same scope as parameters.
+			// A CompoundStmt will not enter a new scope if atFunctionTop is true.
+			ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
+			atFunctionTop = true;
 			maybeMutate_impl( node->statements, *this );
 			maybeMutate_impl( node->attributes, *this );
@@ -1007,9 +1016,9 @@
 	VISIT_START( node );
 	{
-		// do not enter a new scope if inFunction is true - needs to check old state before the assignment
-		ValueGuard< bool > oldInFunction( inFunction );
-		auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeLeave(); } );
+		// Do not enter a new scope if atFunctionTop is true, don't leave one either.
+		ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
+		auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go ) indexerScopeLeave(); } );
 		auto guard2 = makeFuncGuard( [this]() { call_beginScope();   }, [this]() { call_endScope();     } );
-		inFunction = false;
+		atFunctionTop = false;
 		visitStatementList( node->kids );
 	}
@@ -1021,9 +1030,9 @@
 	VISIT_START( node );
 	{
-		// do not enter a new scope if inFunction is true - needs to check old state before the assignment
-		ValueGuard< bool > oldInFunction( inFunction );
-		auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeLeave(); } );
+		// Do not enter a new scope if atFunctionTop is true, don't leave one either.
+		ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
+		auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go ) indexerScopeLeave(); } );
 		auto guard2 = makeFuncGuard( [this]() { call_beginScope();   }, [this]() { call_endScope();     } );
-		inFunction = false;
+		atFunctionTop = false;
 		visitStatementList( node->kids );
 	}
@@ -1035,9 +1044,9 @@
 	MUTATE_START( node );
 	{
-		// do not enter a new scope if inFunction is true - needs to check old state before the assignment
-		ValueGuard< bool > oldInFunction( inFunction );
-		auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeLeave(); } );
+		// Do not enter a new scope if atFunctionTop is true, don't leave one either.
+		ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
+		auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go ) indexerScopeLeave(); } );
 		auto guard2 = makeFuncGuard( [this]() { call_beginScope();   }, [this]() { call_endScope();     } );
-		inFunction = false;
+		atFunctionTop = false;
 		mutateStatementList( node->kids );
 	}
Index: src/GenPoly/Specialize.cc
===================================================================
--- src/GenPoly/Specialize.cc	(revision 6a77224bf06bbc28a67a04ce5efe7106814a9045)
+++ src/GenPoly/Specialize.cc	(revision 4ae78c10a7d9e92dd079b2693e1de5ac44e5f368)
@@ -218,4 +218,8 @@
 		thunkFunc->get_attributes().push_back( new Attribute( "unused" ) );
 
+		// Thunks at the global level must be static to avoid collisions between files.
+		// (Conversly thunks inside a function must be unique and not static.)
+		thunkFunc->storageClasses.is_static = !isInFunction();
+
 		// thread thunk parameters into call to actual function, naming thunk parameters as we go
 		UniqueName paramNamer( paramPrefix );
@@ -321,21 +325,7 @@
 	}
 
-	// Fold it into Specialize if we find a good way.
-	struct StaticThunks final : public WithShortCircuiting {
-		void previsit( Declaration * ) {
-			visit_children = false;
-		}
-		void postvisit( FunctionDecl * decl ) {
-			if ( isPrefix( decl->name, "_thunk" ) ) {
-				decl->storageClasses.is_static = true;
-			}
-		}
-	};
-
 	void convertSpecializations( std::list< Declaration* >& translationUnit ) {
 		PassVisitor<Specialize> spec;
 		mutateAll( translationUnit, spec );
-		PassVisitor<StaticThunks> staticThunks;
-		acceptAll( translationUnit, staticThunks );
 	}
 } // namespace GenPoly
