Index: src/ResolvExpr/AdjustExprType.cc
===================================================================
--- src/ResolvExpr/AdjustExprType.cc	(revision d9fa60af0bc172d6842f414cb608e0615d3582a5)
+++ src/ResolvExpr/AdjustExprType.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -97,5 +97,5 @@
 		EqvClass eqvClass;
 		if ( env.lookup( typeInst->get_name(), eqvClass ) ) {
-			if ( eqvClass.kind == TypeDecl::Ftype ) {
+			if ( eqvClass.data.kind == TypeDecl::Ftype ) {
 				PointerType *pointerType = new PointerType( Type::Qualifiers(), typeInst );
 				return pointerType;
Index: src/ResolvExpr/AlternativeFinder.cc
===================================================================
--- src/ResolvExpr/AlternativeFinder.cc	(revision d9fa60af0bc172d6842f414cb608e0615d3582a5)
+++ src/ResolvExpr/AlternativeFinder.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -373,5 +373,5 @@
 	void makeUnifiableVars( Type *type, OpenVarSet &unifiableVars, AssertionSet &needAssertions ) {
 		for ( Type::ForallList::const_iterator tyvar = type->get_forall().begin(); tyvar != type->get_forall().end(); ++tyvar ) {
-			unifiableVars[ (*tyvar)->get_name() ] = (*tyvar)->get_kind();
+			unifiableVars[ (*tyvar)->get_name() ] = TypeDecl::Data{ *tyvar };
 			for ( std::list< DeclarationWithType* >::iterator assert = (*tyvar)->get_assertions().begin(); assert != (*tyvar)->get_assertions().end(); ++assert ) {
 				needAssertions[ *assert ] = true;
Index: src/ResolvExpr/CommonType.cc
===================================================================
--- src/ResolvExpr/CommonType.cc	(revision d9fa60af0bc172d6842f414cb608e0615d3582a5)
+++ src/ResolvExpr/CommonType.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -57,11 +57,9 @@
 		Type *result = visitor.get_result();
 		if ( ! result ) {
+			// this appears to be handling for opaque type declarations
 			if ( widenSecond ) {
-				TypeInstType *inst = dynamic_cast< TypeInstType* >( type2 );
-				if ( inst ) {
-					NamedTypeDecl *nt = indexer.lookupType( inst->get_name() );
-					if ( nt ) {
-						TypeDecl *type = dynamic_cast< TypeDecl* >( nt );
-						assert( type );
+				if ( TypeInstType *inst = dynamic_cast< TypeInstType* >( type2 ) ) {
+					if ( NamedTypeDecl *nt = indexer.lookupType( inst->get_name() ) ) {
+						TypeDecl *type = safe_dynamic_cast< TypeDecl* >( nt );
 						if ( type->get_base() ) {
 							Type::Qualifiers tq1 = type1->get_qualifiers(), tq2 = type2->get_qualifiers();
@@ -145,7 +143,23 @@
 	}
 
+	/// true if a common type for t must be a complete type
+	bool requiredComplete( Type * t ) {
+		if ( TypeInstType * inst = dynamic_cast< TypeInstType * > ( t ) ) {
+			return inst->get_baseType()->isComplete();
+		}
+		return false;
+	}
+
 	void CommonType::visit( PointerType *pointerType ) {
 		if ( PointerType *otherPointer = dynamic_cast< PointerType* >( type2 ) ) {
-			if ( widenFirst && dynamic_cast< VoidType* >( otherPointer->get_base() ) && ! isFtype(pointerType->get_base(), indexer) ) {
+			// Note: relationship between formal and actual types is antisymmetric
+			//   void free(void *);
+			//   forall(otype T) void foo(T *);
+			//
+			// should be able to pass T* to free, but should not be able to pass a void* to foo.
+			// because of this, the requiredComplete check occurs only on the first, since it corresponds
+			// to the formal parameter type (though this may be incorrect in some cases, in which case
+			// perhaps the widen mode needs to incorporate another bit for whether this is a formal or actual context)
+			if ( widenFirst && dynamic_cast< VoidType* >( otherPointer->get_base() ) && ! isFtype(pointerType->get_base(), indexer) && ! requiredComplete( pointerType->get_base() ) ) {
 				result = otherPointer->clone();
 				result->get_qualifiers() += pointerType->get_qualifiers();
@@ -211,6 +225,5 @@
 			NamedTypeDecl *nt = indexer.lookupType( inst->get_name() );
 			if ( nt ) {
-				TypeDecl *type = dynamic_cast< TypeDecl* >( nt );
-				assert( type );
+				TypeDecl *type = safe_dynamic_cast< TypeDecl* >( nt );
 				if ( type->get_base() ) {
 					Type::Qualifiers tq1 = inst->get_qualifiers(), tq2 = type2->get_qualifiers();
Index: src/ResolvExpr/FindOpenVars.cc
===================================================================
--- src/ResolvExpr/FindOpenVars.cc	(revision d9fa60af0bc172d6842f414cb608e0615d3582a5)
+++ src/ResolvExpr/FindOpenVars.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -48,5 +48,5 @@
 		if ( nextIsOpen ) {
 			for ( Type::ForallList::const_iterator i = type->get_forall().begin(); i != type->get_forall().end(); ++i ) {
-				openVars[ (*i)->get_name() ] = (*i)->get_kind();
+				openVars[ (*i)->get_name() ] = TypeDecl::Data{ (*i) };
 				for ( std::list< DeclarationWithType* >::const_iterator assert = (*i)->get_assertions().begin(); assert != (*i)->get_assertions().end(); ++assert ) {
 					needAssertions[ *assert ] = false;
@@ -57,5 +57,5 @@
 		} else {
 			for ( Type::ForallList::const_iterator i = type->get_forall().begin(); i != type->get_forall().end(); ++i ) {
-				closedVars[ (*i)->get_name() ] = (*i)->get_kind();
+				closedVars[ (*i)->get_name() ] = TypeDecl::Data{ (*i) };
 				for ( std::list< DeclarationWithType* >::const_iterator assert = (*i)->get_assertions().begin(); assert != (*i)->get_assertions().end(); ++assert ) {
 					haveAssertions[ *assert ] = false;
Index: src/ResolvExpr/PtrsCastable.cc
===================================================================
--- src/ResolvExpr/PtrsCastable.cc	(revision d9fa60af0bc172d6842f414cb608e0615d3582a5)
+++ src/ResolvExpr/PtrsCastable.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// PtrsCastable.cc -- 
+// PtrsCastable.cc --
 //
 // Author           : Richard C. Bilson
@@ -25,5 +25,5 @@
 	  public:
 		PtrsCastable( Type *dest, const TypeEnvironment &env, const SymTab::Indexer &indexer );
-  
+
 		int get_result() const { return result; }
 
@@ -61,5 +61,5 @@
 				} //if
 			} else if ( env.lookup( typeInst->get_name(), eqvClass ) ) {
-				if ( eqvClass.kind == TypeDecl::Ftype ) {
+				if ( eqvClass.data.kind == TypeDecl::Ftype ) {
 					return -1;
 				} // if
Index: src/ResolvExpr/TypeEnvironment.cc
===================================================================
--- src/ResolvExpr/TypeEnvironment.cc	(revision d9fa60af0bc172d6842f414cb608e0615d3582a5)
+++ src/ResolvExpr/TypeEnvironment.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -94,5 +94,5 @@
 		dest.type = maybeClone( src.type );
 		dest.allowWidening = src.allowWidening;
-		dest.kind = src.kind;
+		dest.data = src.data;
 	}
 
@@ -162,5 +162,5 @@
 			EqvClass newClass;
 			newClass.vars.insert( (*i)->get_name() );
-			newClass.kind = (*i)->get_kind();
+			newClass.data = TypeDecl::Data{ (*i) };
 			env.push_back( newClass );
 		} // for
@@ -177,5 +177,5 @@
 					sub.add( *theVar, theClass->type );
 				} else if ( theVar != theClass->vars.begin() ) {
-					TypeInstType *newTypeInst = new TypeInstType( Type::Qualifiers(), *theClass->vars.begin(), theClass->kind == TypeDecl::Ftype );
+					TypeInstType *newTypeInst = new TypeInstType( Type::Qualifiers(), *theClass->vars.begin(), theClass->data.kind == TypeDecl::Ftype );
 ///         std::cout << " bound to variable " << *theClass->vars.begin() << std::endl;
 					sub.add( *theVar, newTypeInst );
@@ -243,5 +243,5 @@
 		for ( std::list< EqvClass >::const_iterator eqvClass = env.begin(); eqvClass != env.end(); ++eqvClass ) {
 			for ( std::set< std::string >::const_iterator var = eqvClass->vars.begin(); var != eqvClass->vars.end(); ++var ) {
-				openVars[ *var ] = eqvClass->kind;
+				openVars[ *var ] = eqvClass->data;
 			} // for
 		} // for
Index: src/ResolvExpr/TypeEnvironment.h
===================================================================
--- src/ResolvExpr/TypeEnvironment.h	(revision d9fa60af0bc172d6842f414cb608e0615d3582a5)
+++ src/ResolvExpr/TypeEnvironment.h	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -32,5 +32,5 @@
 	};
 	typedef std::map< DeclarationWithType*, bool, AssertCompare > AssertionSet;
-	typedef std::map< std::string, TypeDecl::Kind > OpenVarSet;
+	typedef std::map< std::string, TypeDecl::Data > OpenVarSet;
 
 	void printAssertionSet( const AssertionSet &, std::ostream &, int indent = 0 );
@@ -41,5 +41,5 @@
 		Type *type;
 		bool allowWidening;
-		TypeDecl::Kind kind;
+		TypeDecl::Data data;
 
 		void initialize( const EqvClass &src, EqvClass &dest );
Index: src/ResolvExpr/Unify.cc
===================================================================
--- src/ResolvExpr/Unify.cc	(revision d9fa60af0bc172d6842f414cb608e0615d3582a5)
+++ src/ResolvExpr/Unify.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -109,13 +109,13 @@
 		newFirst->get_qualifiers() = Type::Qualifiers();
 		newSecond->get_qualifiers() = Type::Qualifiers();
-///   std::cout << "first is ";
-///   first->print( std::cout );
-///   std::cout << std::endl << "second is ";
-///   second->print( std::cout );
-///   std::cout << std::endl << "newFirst is ";
-///   newFirst->print( std::cout );
-///   std::cout << std::endl << "newSecond is ";
-///   newSecond->print( std::cout );
-///   std::cout << std::endl;
+///   std::cerr << "first is ";
+///   first->print( std::cerr );
+///   std::cerr << std::endl << "second is ";
+///   second->print( std::cerr );
+///   std::cerr << std::endl << "newFirst is ";
+///   newFirst->print( std::cerr );
+///   std::cerr << std::endl << "newSecond is ";
+///   newSecond->print( std::cerr );
+///   std::cerr << std::endl;
 		bool result = unifyExact( newFirst, newSecond, newEnv, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
 		delete newFirst;
@@ -133,10 +133,39 @@
 	}
 
-	bool tyVarCompatible( TypeDecl::Kind kind, Type *type, const SymTab::Indexer &indexer ) {
-		switch ( kind ) {
+	struct CompleteTypeChecker : public Visitor {
+		virtual void visit( VoidType *basicType ) { status = false; }
+		virtual void visit( BasicType *basicType ) {}
+		virtual void visit( PointerType *pointerType ) {}
+		virtual void visit( ArrayType *arrayType ) { status = ! arrayType->get_isVarLen(); }
+		virtual void visit( FunctionType *functionType ) {}
+		virtual void visit( StructInstType *aggregateUseType ) { status = aggregateUseType->get_baseStruct()->has_body(); }
+		virtual void visit( UnionInstType *aggregateUseType ) { status = aggregateUseType->get_baseUnion()->has_body(); }
+		// xxx - enum inst does not currently contain a pointer to base, this should be fixed.
+		virtual void visit( EnumInstType *aggregateUseType ) { /* status = aggregateUseType->get_baseEnum()->hasBody(); */ }
+		virtual void visit( TraitInstType *aggregateUseType ) { assert( false ); }
+		virtual void visit( TypeInstType *aggregateUseType ) { status = aggregateUseType->get_baseType()->isComplete(); }
+		virtual void visit( TupleType *tupleType ) {} // xxx - not sure if this is right, might need to recursively check complete-ness
+		virtual void visit( TypeofType *typeofType ) { assert( false ); }
+		virtual void visit( AttrType *attrType ) { assert( false ); } // xxx - not sure what to do here
+		virtual void visit( VarArgsType *varArgsType ){} // xxx - is this right?
+		virtual void visit( ZeroType *zeroType ) {}
+		virtual void visit( OneType *oneType ) {}
+		bool status = true;
+	};
+	bool isComplete( Type * type ) {
+		CompleteTypeChecker checker;
+		assert( type );
+		type->accept( checker );
+		return checker.status;
+	}
+
+	bool tyVarCompatible( const TypeDecl::Data & data, Type *type, const SymTab::Indexer &indexer ) {
+		switch ( data.kind ) {
 		  case TypeDecl::Any:
 		  case TypeDecl::Dtype:
-			return ! isFtype( type, indexer );
-
+			// to bind to an object type variable, the type must not be a function type.
+			// if the type variable is specified to be a complete type then the incoming
+			// type must also be complete
+			return ! isFtype( type, indexer ) && (! data.isComplete || isComplete( type ));
 		  case TypeDecl::Ftype:
 			return isFtype( type, indexer );
@@ -146,5 +175,5 @@
 	}
 
-	bool bindVar( TypeInstType *typeInst, Type *other, TypeDecl::Kind kind, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, WidenMode widenMode, const SymTab::Indexer &indexer ) {
+	bool bindVar( TypeInstType *typeInst, Type *other, const TypeDecl::Data & data, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, WidenMode widenMode, const SymTab::Indexer &indexer ) {
 		OpenVarSet::const_iterator tyvar = openVars.find( typeInst->get_name() );
 		assert( tyvar != openVars.end() );
@@ -185,5 +214,5 @@
 			newClass.type->get_qualifiers() = Type::Qualifiers();
 			newClass.allowWidening = widenMode.widenFirst && widenMode.widenSecond;
-			newClass.kind = kind;
+			newClass.data = data;
 			env.add( newClass );
 		} // if
@@ -191,5 +220,5 @@
 	}
 
-	bool bindVarToVar( TypeInstType *var1, TypeInstType *var2, TypeDecl::Kind kind, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, WidenMode widenMode, const SymTab::Indexer &indexer ) {
+	bool bindVarToVar( TypeInstType *var1, TypeInstType *var2, const TypeDecl::Data & data, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, WidenMode widenMode, const SymTab::Indexer &indexer ) {
 		bool result = true;
 		EqvClass class1, class2;
@@ -220,5 +249,5 @@
 
 		if ( type1 && type2 ) {
-//    std::cout << "has type1 && type2" << std::endl;
+//    std::cerr << "has type1 && type2" << std::endl;
 			WidenMode newWidenMode ( widen1, widen2 );
 			Type *common = 0;
@@ -258,5 +287,5 @@
 			newClass.vars.insert( var2->get_name() );
 			newClass.allowWidening = widen1 && widen2;
-			newClass.kind = kind;
+			newClass.data = data;
 			env.add( newClass );
 		} // if
@@ -321,16 +350,16 @@
 		} // if
 #ifdef DEBUG
-		std::cout << "============ unifyExact" << std::endl;
-		std::cout << "type1 is ";
-		type1->print( std::cout );
-		std::cout << std::endl << "type2 is ";
-		type2->print( std::cout );
-		std::cout << std::endl << "openVars are ";
-		printOpenVarSet( openVars, std::cout, 8 );
-		std::cout << std::endl << "input env is " << std::endl;
-		debugEnv.print( std::cout, 8 );
-		std::cout << std::endl << "result env is " << std::endl;
-		env.print( std::cout, 8 );
-		std::cout << "result is " << result << std::endl;
+		std::cerr << "============ unifyExact" << std::endl;
+		std::cerr << "type1 is ";
+		type1->print( std::cerr );
+		std::cerr << std::endl << "type2 is ";
+		type2->print( std::cerr );
+		std::cerr << std::endl << "openVars are ";
+		printOpenVarSet( openVars, std::cerr, 8 );
+		std::cerr << std::endl << "input env is " << std::endl;
+		debugEnv.print( std::cerr, 8 );
+		std::cerr << std::endl << "result env is " << std::endl;
+		env.print( std::cerr, 8 );
+		std::cerr << "result is " << result << std::endl;
 #endif
 		return result;
@@ -347,25 +376,25 @@
 		bool result;
 #ifdef DEBUG
-		std::cout << "unifyInexact type 1 is ";
-		type1->print( std::cout );
-		std::cout << "type 2 is ";
-		type2->print( std::cout );
-		std::cout << std::endl;
+		std::cerr << "unifyInexact type 1 is ";
+		type1->print( std::cerr );
+		std::cerr << "type 2 is ";
+		type2->print( std::cerr );
+		std::cerr << std::endl;
 #endif
 		if ( ! unifyExact( type1, type2, env, needAssertions, haveAssertions, openVars, widenMode, indexer ) ) {
 #ifdef DEBUG
-			std::cout << "unifyInexact: no exact unification found" << std::endl;
+			std::cerr << "unifyInexact: no exact unification found" << std::endl;
 #endif
 			if ( ( common = commonType( type1, type2, widenMode.widenFirst, widenMode.widenSecond, indexer, env, openVars ) ) ) {
 				common->get_qualifiers() = tq1 + tq2;
 #ifdef DEBUG
-				std::cout << "unifyInexact: common type is ";
-				common->print( std::cout );
-				std::cout << std::endl;
+				std::cerr << "unifyInexact: common type is ";
+				common->print( std::cerr );
+				std::cerr << std::endl;
 #endif
 				result = true;
 			} else {
 #ifdef DEBUG
-				std::cout << "unifyInexact: no common type found" << std::endl;
+				std::cerr << "unifyInexact: no common type found" << std::endl;
 #endif
 				result = false;
@@ -404,12 +433,12 @@
 
 	void markAssertionSet( AssertionSet &assertions, DeclarationWithType *assert ) {
-///   std::cout << "assertion set is" << std::endl;
-///   printAssertionSet( assertions, std::cout, 8 );
-///   std::cout << "looking for ";
-///   assert->print( std::cout );
-///   std::cout << std::endl;
+///   std::cerr << "assertion set is" << std::endl;
+///   printAssertionSet( assertions, std::cerr, 8 );
+///   std::cerr << "looking for ";
+///   assert->print( std::cerr );
+///   std::cerr << std::endl;
 		AssertionSet::iterator i = assertions.find( assert );
 		if ( i != assertions.end() ) {
-///     std::cout << "found it!" << std::endl;
+///     std::cerr << "found it!" << std::endl;
 			i->second = true;
 		} // if
