Index: src/Concurrency/Keywords.cpp
===================================================================
--- src/Concurrency/Keywords.cpp	(revision 6018ddb9b78727195a7bc9a68a40e3fb3449f935)
+++ src/Concurrency/Keywords.cpp	(revision 44940ee7f65127636046669347564cbe8f13462e)
@@ -29,5 +29,4 @@
 #include "Common/Examine.hpp"
 #include "Common/Utility.hpp"
-#include "Concurrency/MutexFuncHash.hpp"
 #include "Common/UniqueName.hpp"
 #include "ControlStruct/LabelGenerator.hpp"
@@ -1078,5 +1077,5 @@
 
 	// In reverse order:
-	// monitor_dtor_guard_t __guard = { __monitor, func, func_id, false };
+	// monitor_dtor_guard_t __guard = { __monitor, func, false };
 	mutBody->push_front(
 		new ast::DeclStmt( location, new ast::ObjectDecl(
@@ -1095,6 +1094,4 @@
 							generic_func,
 							ast::ExplicitCast ) ),
-					new ast::SingleInit( location,
-						Concurrency::hashMangleExpr( location, func ) ),
 					new ast::SingleInit( location,
 						ast::ConstantExpr::from_bool( location, false ) ),
@@ -1178,6 +1175,4 @@
 						ast::ExplicitCast
 					) ),
-					new ast::SingleInit( location,
-						Concurrency::hashMangleExpr( location, func ) ),
 				},
 				{},
@@ -1480,5 +1475,5 @@
 
 ast::CompoundStmt * MutexKeyword::addThreadDtorStatements(
-		const ast::FunctionDecl* func, const ast::CompoundStmt * body,
+		const ast::FunctionDecl*, const ast::CompoundStmt * body,
 		const std::vector<const ast::DeclWithType * > & args ) {
 	assert( args.size() == 1 );
@@ -1492,5 +1487,5 @@
 	const CodeLocation & location = mutBody->location;
 
-	// thread_dtor_guard_t __guard = { this, func_id, intptr( 0 ) };
+	// thread_dtor_guard_t __guard = { this, intptr( 0 ) };
 	mutBody->push_front( new ast::DeclStmt(
 		location,
@@ -1505,6 +1500,4 @@
 						new ast::CastExpr( location,
 							new ast::VariableExpr( location, arg ), argType ) ),
-					new ast::SingleInit( location,
-						Concurrency::hashMangleExpr( location, func ) ),
 					new ast::SingleInit(
 						location,
Index: src/Concurrency/MutexFuncHash.hpp
===================================================================
--- src/Concurrency/MutexFuncHash.hpp	(revision 6018ddb9b78727195a7bc9a68a40e3fb3449f935)
+++ 	(revision )
@@ -1,50 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// MutexFuncHash.hpp -- Hash utility for mutex function identity.
-//
-// Author           : Matthew Au-Yeung
-// Created On       : Tue Jan 28 2026
-//
-
-#pragma once
-
-#include <cstdint>
-#include <string>
-
-#include "AST/Decl.hpp"
-#include "AST/Expr.hpp"
-#include "AST/Type.hpp"
-#include "SymTab/Mangler.hpp"
-
-namespace Concurrency {
-
-// FNV-1a hash of a function declaration's mangled name.
-// Used to identify mutex functions across translation units,
-// since function pointers may differ for static inline functions.
-static inline uint64_t hashMangle( const ast::DeclWithType * decl ) {
-	std::string name = Mangle::mangle( decl );
-	uint64_t hash = 14695981039346656037ULL; // FNV offset basis
-	for ( char c : name ) {
-		hash ^= static_cast<uint64_t>( c );
-		hash *= 1099511628211ULL; // FNV prime
-	}
-	return hash;
-}
-
-// Create a ConstantExpr for the hash with proper ULL suffix to avoid
-// C compiler warnings about large unsigned constants.
-static inline ast::ConstantExpr * hashMangleExpr(
-		const CodeLocation & location, const ast::DeclWithType * decl ) {
-	uint64_t hash = hashMangle( decl );
-	return new ast::ConstantExpr{
-		location,
-		new ast::BasicType{ ast::BasicKind::LongLongUnsignedInt },
-		std::to_string( hash ) + "ull",
-		(unsigned long long)hash };
-}
-
-} // namespace Concurrency
Index: src/Concurrency/Waitfor.cpp
===================================================================
--- src/Concurrency/Waitfor.cpp	(revision 6018ddb9b78727195a7bc9a68a40e3fb3449f935)
+++ src/Concurrency/Waitfor.cpp	(revision 44940ee7f65127636046669347564cbe8f13462e)
@@ -22,5 +22,4 @@
 #include "InitTweak/InitTweak.hpp"
 #include "ResolvExpr/Resolver.hpp"
-#include "Concurrency/MutexFuncHash.hpp"
 
 #include "AST/Print.hpp"
@@ -332,8 +331,4 @@
 		makeAccStmt( location, acceptables, index, "func",
 			funcExpr, context ),
-		makeAccStmt( location, acceptables, index, "func_id",
-			Concurrency::hashMangleExpr( location,
-				variableExpr->var.strict_as<ast::DeclWithType>() ),
-			context ),
 		makeAccStmt( location, acceptables, index, "data",
 			new ast::VariableExpr( location, monitors ), context ),
Index: src/Validate/Autogen.cpp
===================================================================
--- src/Validate/Autogen.cpp	(revision 6018ddb9b78727195a7bc9a68a40e3fb3449f935)
+++ src/Validate/Autogen.cpp	(revision 44940ee7f65127636046669347564cbe8f13462e)
@@ -402,5 +402,17 @@
 	}
 
-	return genProto( "^?{}", { dst }, {} );
+	ast::FunctionDecl * decl = genProto( "^?{}", { dst }, {} );
+	// For concurrent types, remove static storage and inline specifier, and add
+	// cfa_linkonce attribute so the destructor has external linkage with linkonce
+	// semantics. This is required for waitfor to work correctly across translation
+	// units - the function pointer must be the same everywhere, and cfa_linkonce
+	// ensures only one definition survives linking.
+	if ( isConcurrentType() ) {
+		auto mut = ast::mutate( decl );
+		mut->storage = ast::Storage::Classes();
+		mut->funcSpec = ast::Function::Specs();
+		mut->attributes.push_back( new ast::Attribute( "cfa_linkonce" ) );
+	}
+	return decl;
 }
 
