Index: src/ResolvExpr/CandidateFinder.cpp
===================================================================
--- src/ResolvExpr/CandidateFinder.cpp	(revision e00c22feb84a430a60c57638aabf2d3f11f8c449)
+++ src/ResolvExpr/CandidateFinder.cpp	(revision e5c38112cf0116f0fcdd3e08dd4133429b2ecfbe)
@@ -43,4 +43,7 @@
 #include "SymTab/Validate.h"      // for validateType
 #include "Tuples/Tuples.h"        // for handleTupleAssignment
+#include "InitTweak/InitTweak.h"  // for getPointerBase
+
+#include "Common/Stats/Counter.h"
 
 #define PRINT( text ) if ( resolvep ) { text }
@@ -864,11 +867,4 @@
 
 		void postvisit( const ast::UntypedExpr * untypedExpr ) {
-			CandidateFinder funcFinder{ symtab, tenv };
-			funcFinder.find( untypedExpr->func, ResolvMode::withAdjustment() );
-			// short-circuit if no candidates
-			if ( funcFinder.candidates.empty() ) return;
-
-			reason.code = NoMatch;
-
 			std::vector< CandidateFinder > argCandidates =
 				selfFinder.findSubExprs( untypedExpr->args );
@@ -877,4 +873,43 @@
 			// if not tuple assignment, handled as normal function call
 			Tuples::handleTupleAssignment( selfFinder, untypedExpr, argCandidates );
+
+			CandidateFinder funcFinder{ symtab, tenv };
+			if (auto nameExpr = untypedExpr->func.as<ast::NameExpr>()) {
+				auto kind = ast::SymbolTable::getSpecialFunctionKind(nameExpr->name);
+				if (kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS) {
+					assertf(!argCandidates.empty(), "special function call without argument");
+					for (auto & firstArgCand: argCandidates[0]) {
+						ast::ptr<ast::Type> argType = firstArgCand->expr->result;
+						firstArgCand->env.apply(argType);
+						// strip references
+						// xxx - is this correct?
+						while (argType.as<ast::ReferenceType>()) argType = argType.as<ast::ReferenceType>()->base;
+
+						// convert 1-tuple to plain type
+						if (auto tuple = argType.as<ast::TupleType>()) {
+							if (tuple->size() == 1) {
+								argType = tuple->types[0];
+							}
+						}
+						
+						// if argType is an unbound type parameter, all special functions need to be searched.
+						if (isUnboundType(argType)) {
+							funcFinder.otypeKeys.clear();
+							break;
+						}
+
+						if (argType.as<ast::PointerType>()) funcFinder.otypeKeys.insert(Mangle::Encoding::pointer);
+						else funcFinder.otypeKeys.insert(Mangle::mangle(argType, Mangle::NoGenericParams | Mangle::Type));
+					}
+				}
+			}
+			// if candidates are already produced, do not fail
+			// xxx - is it possible that handleTupleAssignment and main finder both produce candidates?
+			// this means there exists ctor/assign functions with a tuple as first parameter.
+			funcFinder.find( untypedExpr->func, selfFinder.candidates.empty() ? ResolvMode::withAdjustment() : ResolvMode::withoutFailFast() );
+			// short-circuit if no candidates
+			// if ( funcFinder.candidates.empty() ) return;
+
+			reason.code = NoMatch;
 
 			// find function operators
@@ -1187,6 +1222,19 @@
 
 		void postvisit( const ast::NameExpr * nameExpr ) {
-			std::vector< ast::SymbolTable::IdData > declList = symtab.lookupId( nameExpr->name );
+			std::vector< ast::SymbolTable::IdData > declList;
+			if (!selfFinder.otypeKeys.empty()) {
+				auto kind = ast::SymbolTable::getSpecialFunctionKind(nameExpr->name);
+				assertf(kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS, "special lookup with non-special target: %s", nameExpr->name.c_str());
+
+				for (auto & otypeKey: selfFinder.otypeKeys) {
+					auto result = symtab.specialLookupId(kind, otypeKey);
+					declList.insert(declList.end(), std::make_move_iterator(result.begin()), std::make_move_iterator(result.end()));
+				}
+			}
+			else {
+				declList = symtab.lookupId( nameExpr->name );
+			}
 			PRINT( std::cerr << "nameExpr is " << nameExpr->name << std::endl; )
+
 			if( declList.empty() ) return;
 
@@ -1558,4 +1606,5 @@
 					}
 				}
+
 			}
 
Index: src/ResolvExpr/CandidateFinder.hpp
===================================================================
--- src/ResolvExpr/CandidateFinder.hpp	(revision e00c22feb84a430a60c57638aabf2d3f11f8c449)
+++ src/ResolvExpr/CandidateFinder.hpp	(revision e5c38112cf0116f0fcdd3e08dd4133429b2ecfbe)
@@ -31,4 +31,5 @@
 	const ast::TypeEnvironment & env;  ///< Substitutions performed in this resolution
 	ast::ptr< ast::Type > targetType;  ///< Target type for resolution
+	std::set< std::string > otypeKeys;  /// different type may map to same key
 
 	CandidateFinder(
Index: src/ResolvExpr/SatisfyAssertions.cpp
===================================================================
--- src/ResolvExpr/SatisfyAssertions.cpp	(revision e00c22feb84a430a60c57638aabf2d3f11f8c449)
+++ src/ResolvExpr/SatisfyAssertions.cpp	(revision e5c38112cf0116f0fcdd3e08dd4133429b2ecfbe)
@@ -167,5 +167,24 @@
 		// find candidates that unify with the desired type
 		AssnCandidateList matches;
-		for ( const ast::SymbolTable::IdData & cdata : sat.symtab.lookupId( assn.first->name ) ) {
+
+		std::vector<ast::SymbolTable::IdData> candidates;
+		auto kind = ast::SymbolTable::getSpecialFunctionKind(assn.first->name);
+		if (kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS) {
+			// prefilter special decls by argument type, if already known
+			ast::ptr<ast::Type> thisArgType = strict_dynamic_cast<const ast::PointerType *>(assn.first->get_type())->base
+				.strict_as<ast::FunctionType>()->params[0]
+				.strict_as<ast::ReferenceType>()->base;
+			sat.cand->env.apply(thisArgType);
+
+			std::string otypeKey = "";
+			if (thisArgType.as<ast::PointerType>()) otypeKey = Mangle::Encoding::pointer;
+			else if (!isUnboundType(thisArgType)) otypeKey = Mangle::mangle(thisArgType, Mangle::Type | Mangle::NoGenericParams);
+
+			candidates = sat.symtab.specialLookupId(kind, otypeKey);
+		}
+		else {
+			candidates = sat.symtab.lookupId(assn.first->name);
+		}
+		for ( const ast::SymbolTable::IdData & cdata : candidates ) {
 			const ast::DeclWithType * candidate = cdata.id;
 
