Index: src/CodeGen/LinkOnce.cpp
===================================================================
--- src/CodeGen/LinkOnce.cpp	(revision 88bb0b48843bb86ea654ab03f597d6170e759cd1)
+++ src/CodeGen/LinkOnce.cpp	(revision d8a307394c8bcba5ac53fe0e01cfec5e84cc368a)
@@ -22,4 +22,6 @@
 #include "AST/Expr.hpp"
 #include "AST/Pass.hpp"
+#include "CompilationState.hpp"
+#include "InitTweak/InitTweak.hpp"
 
 namespace CodeGen {
@@ -33,4 +35,8 @@
 bool is_section_attribute( ast::Attribute const * attr ) {
 	return "section" == attr->name;
+}
+
+bool is_visibility_attribute( ast::Attribute const * attr ) {
+	return "visibility" == attr->name;
 }
 
@@ -60,5 +66,4 @@
 		ast::Attribute * attribute = found->get_and_mutate();
 		assert( attribute->params.empty() );
-		assert( !decl->mangleName.empty() );
 
 		attribute->name = "section";
@@ -69,7 +74,52 @@
 		);
 
-		// Unconditionnaly add "visibility(default)" to anything with
-		// .gnu.linkonce visibility is a mess otherwise.
 		attributes.push_back( new ast::Attribute( "visibility", {
+			ast::ConstantExpr::from_string( mutDecl->location, "default" )
+		} ) );
+
+		// Mark as used so GCC does not warn about unused definitions
+		// in translation units that include but don't call the function.
+		attributes.push_back( new ast::Attribute( "used" ) );
+
+		return mutDecl;
+	}
+};
+
+/// Adds visibility("default") to autogen CFA autogen function definitions so they
+/// are exported from shared libraries compiled with -fvisibility=hidden.
+struct AutogenDefaultVisibility {
+	int funcDepth = 0;
+
+	void previsit( ast::FunctionDecl const * ) {
+		funcDepth++;
+	}
+
+	ast::FunctionDecl const * postvisit( ast::FunctionDecl const * decl ) {
+		funcDepth--;
+		// Only top-level function definitions (not nested).
+		if ( funcDepth > 0 ) return decl;
+		// Only CFA-mangled, non-builtin, non-static, non-inline functions.
+		if ( !decl->linkage.is_mangled ) return decl;
+		if ( decl->linkage.is_builtin ) return decl;
+		if ( decl->storage.is_static || decl->funcSpec.is_inline ) return decl;
+
+		// Only definitions (functions with bodies).
+		if ( !decl->stmts ) return decl;
+
+		// Only constructors, destructors, and assignment operators —
+		// the functions that cfa_linkonce autogen functions call on member types.
+		if ( !InitTweak::isDefaultConstructor( decl )
+				&& !InitTweak::isCopyConstructor( decl )
+				&& !InitTweak::isDestructor( decl )
+				&& !InitTweak::isAssignment( decl ) ) return decl;
+
+		// Skip if already has a visibility attribute.
+		auto & attributes = decl->attributes;
+		auto found = std::find_if( attributes.begin(), attributes.end(),
+				is_visibility_attribute );
+		if ( attributes.end() != found ) return decl;
+
+		auto mutDecl = mutate( decl );
+		mutDecl->attributes.push_back( new ast::Attribute( "visibility", {
 			ast::ConstantExpr::from_string( mutDecl->location, "default" )
 		} ) );
@@ -82,4 +132,10 @@
 void translateLinkOnce( ast::TranslationUnit & translationUnit ) {
 	ast::Pass<LinkOnceCore>::run( translationUnit );
+
+	// When building libcfa (-cfalib), add visibility("default") to CFA
+	// function definitions so they are exported despite -fvisibility=hidden.
+	if ( buildingLibrary() ) {
+		ast::Pass<AutogenDefaultVisibility>::run( translationUnit );
+	}
 }
 
Index: src/Validate/Autogen.cpp
===================================================================
--- src/Validate/Autogen.cpp	(revision 88bb0b48843bb86ea654ab03f597d6170e759cd1)
+++ src/Validate/Autogen.cpp	(revision d8a307394c8bcba5ac53fe0e01cfec5e84cc368a)
@@ -352,4 +352,23 @@
 	replaceAll( assertions, oldToNew );
 
+	bool isTopLevel = ( 0 == functionNesting );
+	bool isCfaLinkage = getDecl()->linkage.is_mangled;
+
+	ast::Storage::Classes storage = ast::Storage::Classes(); // Default no static
+	ast::Function::Specs funcSpec; // Default no inline
+	std::vector<ast::ptr<ast::Attribute>> attributes;
+	if ( !isTopLevel ) {
+		// Nested: use inline, no linkonce needed for local types.
+		funcSpec = ast::Function::Specs( ast::Function::Inline );
+	} else if ( isCfaLinkage ) {
+		// Top-level CFA type: use linkonce for cross-TU deduplication.
+		attributes.push_back( new ast::Attribute( "cfa_linkonce" ) );
+	} else {
+		// Top-level C type: keep original static inline to avoid
+		// exposing latent issues in auto-generated code for system types.
+		storage = ast::Storage::Static;
+		funcSpec = ast::Function::Specs( ast::Function::Inline );
+	}
+
 	ast::FunctionDecl * decl = new ast::FunctionDecl(
 		// Auto-generated routines use the type declaration's location.
@@ -362,10 +381,8 @@
 		// Only a prototype, no body.
 		nullptr,
-		// Use static storage if we are at the top level.
-		(0 < functionNesting) ? ast::Storage::Classes() : ast::Storage::Static,
+		storage,
 		proto_linkage,
-		std::vector<ast::ptr<ast::Attribute>>(),
-		// Auto-generated routines are inline to avoid conflicts.
-		ast::Function::Specs( ast::Function::Inline ) );
+		std::move( attributes ),
+		funcSpec );
 	decl->fixUniqueId();
 	return decl;
@@ -401,16 +418,5 @@
 		add_qualifiers( dst->type, ast::CV::Qualifiers( ast::CV::Mutex ) );
 	}
-
-	ast::FunctionDecl * decl = genProto( "^?{}", { dst }, {} );
-	// For concurrent types, remove static storage and inline specifier, and add
-	// cfa_linkonce attribute so the destructor has linkonce semantics.
-	// This is required to share the same function pointer across TUs.
-	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;
+	return genProto( "^?{}", { dst }, {} );
 }
 
@@ -697,6 +703,12 @@
 			new ast::AddressExpr( location,
 				new ast::VariableExpr( location, dstParam ) ),
-			new ast::AddressExpr( location,
-				new ast::VariableExpr( location, srcParam ) ),
+			// For array types, the parameter decays to a pointer, so the
+			// variable already points to the data. For other types, take &src.
+			dynamic_cast<const ast::ArrayType *>( srcParam->type.get() )
+				? (ast::Expr *)new ast::CastExpr( location,
+					new ast::VariableExpr( location, srcParam ),
+					new ast::PointerType( new ast::VoidType() ) )
+				: (ast::Expr *)new ast::AddressExpr( location,
+					new ast::VariableExpr( location, srcParam ) ),
 			new ast::SizeofExpr( location, srcParam->type ),
 		} ) );
