Index: src/CodeGen/CodeGenerator.cc
===================================================================
--- src/CodeGen/CodeGenerator.cc	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/CodeGen/CodeGenerator.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -169,8 +169,8 @@
 			output << aggDecl->get_name();
 
-		std::list< Declaration * > & memb = aggDecl->get_members();
-		if ( ! memb.empty() ) {
-//		if ( aggDecl->has_body() ) {
-//			std::list< Declaration * > & memb = aggDecl->get_members();
+		// std::list< Declaration * > & memb = aggDecl->get_members();
+		// if ( ! memb.empty() ) {
+		if ( aggDecl->has_body() ) {
+			std::list< Declaration * > & memb = aggDecl->get_members();
 			output << " {" << endl;
 
Index: src/GenPoly/Box.cc
===================================================================
--- src/GenPoly/Box.cc	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/GenPoly/Box.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -275,5 +275,5 @@
 
 		for ( std::list< TypeDecl* >::const_iterator decl = decls.begin(); decl != decls.end(); ++decl ) {
-			if ( (*decl)->get_kind() == TypeDecl::Any ) {
+			if ( (*decl)->isComplete() ) {
 				otypeDecls.push_back( *decl );
 			}
@@ -719,5 +719,5 @@
 
 		TypeDecl *Pass1::mutate( TypeDecl *typeDecl ) {
-			scopeTyVars[ typeDecl->get_name() ] = typeDecl->get_kind();
+			addToTyVarMap( typeDecl, scopeTyVars );
 			return Mutator::mutate( typeDecl );
 		}
@@ -774,5 +774,5 @@
 				ResolvExpr::EqvClass eqvClass;
 				assert( env );
-				if ( tyParm->second == TypeDecl::Any ) {
+				if ( tyParm->second.isComplete ) {
 					Type *concrete = env->lookup( tyParm->first );
 					if ( concrete ) {
@@ -1278,5 +1278,5 @@
 			std::list< Expression *>::iterator paramBegin = appExpr->get_args().begin();
 
-			TyVarMap exprTyVars( (TypeDecl::Kind)-1 );
+			TyVarMap exprTyVars( TypeDecl::Data{} );
 			makeTyVarMap( function, exprTyVars );
 			ReferenceToType *dynRetType = isDynRet( function, exprTyVars );
@@ -1428,4 +1428,5 @@
 
 						// skip non-otype parameters (ftype/dtype)
+						// xxx - should this check whether the type is complete instead?
 						if ( (*forallIt)->get_kind() != TypeDecl::Any ) continue;
 
@@ -1553,5 +1554,5 @@
 
 		TypeDecl * Pass2::mutate( TypeDecl *typeDecl ) {
-			scopeTyVars[ typeDecl->get_name() ] = typeDecl->get_kind();
+			addToTyVarMap( typeDecl, scopeTyVars );
 			if ( typeDecl->get_base() ) {
 				return handleDecl( typeDecl, typeDecl->get_base() );
@@ -1597,5 +1598,5 @@
 				ObjectDecl *sizeParm, *alignParm;
 				// add all size and alignment parameters to parameter list
-				if ( (*tyParm)->get_kind() == TypeDecl::Any ) {
+				if ( (*tyParm)->isComplete() ) {
 					TypeInstType parmType( Type::Qualifiers(), (*tyParm)->get_name(), *tyParm );
 					std::string parmName = mangleType( &parmType );
@@ -1706,5 +1707,5 @@
 
 		TypeDecl * PolyGenericCalculator::mutate( TypeDecl *typeDecl ) {
-			scopeTyVars[ typeDecl->get_name() ] = typeDecl->get_kind();
+			addToTyVarMap( typeDecl, scopeTyVars );
 			return Parent::mutate( typeDecl );
 		}
@@ -1870,5 +1871,5 @@
 			for ( ; baseParam != baseParams.end() && typeParam != typeParams.end(); ++baseParam, ++typeParam ) {
 				// skip non-otype parameters
-				if ( (*baseParam)->get_kind() != TypeDecl::Any ) continue;
+				if ( ! (*baseParam)->isComplete() ) continue;
 				TypeExpr *typeExpr = dynamic_cast< TypeExpr* >( *typeParam );
 				assert( typeExpr && "all otype parameters should be type expressions" );
@@ -2082,5 +2083,5 @@
 //   Initializer *init = 0;
 //   std::list< Expression *> designators;
-//   scopeTyVars[ typeDecl->get_name() ] = typeDecl->get_kind();
+//   addToTyVarMap( typeDecl, scopeTyVars );
 //   if ( typeDecl->get_base() ) {
 //     init = new SimpleInit( new SizeofExpr( handleDecl( typeDecl, typeDecl->get_base() ) ), designators );
@@ -2088,5 +2089,5 @@
 //   return new ObjectDecl( typeDecl->get_name(), Declaration::Extern, LinkageSpec::C, 0, new BasicType( Type::Qualifiers(), BasicType::UnsignedInt ), init );
 
-			scopeTyVars[ typeDecl->get_name() ] = typeDecl->get_kind();
+			addToTyVarMap( typeDecl, scopeTyVars );
 			return Mutator::mutate( typeDecl );
 		}
Index: src/GenPoly/GenPoly.cc
===================================================================
--- src/GenPoly/GenPoly.cc	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/GenPoly/GenPoly.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -97,5 +97,5 @@
 		if ( TypeInstType *typeInst = dynamic_cast< TypeInstType * >( type ) ) {
 			auto var = tyVars.find( typeInst->get_name() );
-			if ( var != tyVars.end() && var->second == TypeDecl::Any ) {
+			if ( var != tyVars.end() && var->second.isComplete ) {
 				return typeInst;
 			}
@@ -117,5 +117,5 @@
 		if ( function->get_returnVals().empty() ) return 0;
 
-		TyVarMap forallTypes( (TypeDecl::Kind)-1 );
+		TyVarMap forallTypes( TypeDecl::Data{} );
 		makeTyVarMap( function, forallTypes );
 		return (ReferenceToType*)isDynType( function->get_returnVals().front()->get_type(), forallTypes );
@@ -227,8 +227,12 @@
 	}
 
+	void addToTyVarMap( TypeDecl * tyVar, TyVarMap &tyVarMap ) {
+		tyVarMap[ tyVar->get_name() ] = TypeDecl::Data{ tyVar };
+	}
+
 	void makeTyVarMap( Type *type, TyVarMap &tyVarMap ) {
 		for ( Type::ForallList::const_iterator tyVar = type->get_forall().begin(); tyVar != type->get_forall().end(); ++tyVar ) {
 			assert( *tyVar );
-			tyVarMap[ (*tyVar)->get_name() ] = (*tyVar)->get_kind();
+			addToTyVarMap( *tyVar, tyVarMap );
 		}
 		if ( PointerType *pointer = dynamic_cast< PointerType* >( type ) ) {
Index: src/GenPoly/GenPoly.h
===================================================================
--- src/GenPoly/GenPoly.h	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/GenPoly/GenPoly.h	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -30,5 +30,5 @@
 
 namespace GenPoly {
-	typedef ErasableScopedMap< std::string, TypeDecl::Kind > TyVarMap;
+	typedef ErasableScopedMap< std::string, TypeDecl::Data > TyVarMap;
 
 	/// Replaces a TypeInstType by its referrent in the environment, if applicable
@@ -74,4 +74,7 @@
 	VariableExpr *getBaseVar( Expression *expr, int *levels = 0 );
 
+	/// Adds the type variable `tyVar` to `tyVarMap`
+	void addToTyVarMap( TypeDecl * tyVar, TyVarMap &tyVarMap );
+
 	/// Adds the declarations in the forall list of type (and its pointed-to type if it's a pointer type) to `tyVarMap`
 	void makeTyVarMap( Type *type, TyVarMap &tyVarMap );
Index: src/GenPoly/InstantiateGeneric.cc
===================================================================
--- src/GenPoly/InstantiateGeneric.cc	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/GenPoly/InstantiateGeneric.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -284,4 +284,5 @@
 				// set concDecl to new type, insert type declaration into statements to add
 				concDecl = new StructDecl( typeNamer.newName( inst->get_name() ) );
+				concDecl->set_body( inst->get_baseStruct()->has_body() );
 				substituteMembers( inst->get_baseStruct()->get_members(), *inst->get_baseParameters(), typeSubs, 	concDecl->get_members() );
 				DeclMutator::addDeclaration( concDecl );
@@ -337,4 +338,5 @@
 				// set concDecl to new type, insert type declaration into statements to add
 				concDecl = new UnionDecl( typeNamer.newName( inst->get_name() ) );
+				concDecl->set_body( inst->get_baseUnion()->has_body() );
 				substituteMembers( inst->get_baseUnion()->get_members(), *inst->get_baseParameters(), typeSubs, concDecl->get_members() );
 				DeclMutator::addDeclaration( concDecl );
Index: src/GenPoly/PolyMutator.cc
===================================================================
--- src/GenPoly/PolyMutator.cc	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/GenPoly/PolyMutator.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -23,9 +23,5 @@
 
 namespace GenPoly {
-	namespace {
-		const std::list<Label> noLabels;
-	}
-
-	PolyMutator::PolyMutator() : scopeTyVars( (TypeDecl::Kind)-1 ), env( 0 ) {}
+	PolyMutator::PolyMutator() : scopeTyVars( TypeDecl::Data{} ), env( 0 ) {}
 
 	void PolyMutator::mutateStatementList( std::list< Statement* > &statements ) {
Index: src/GenPoly/ScrubTyVars.cc
===================================================================
--- src/GenPoly/ScrubTyVars.cc	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/GenPoly/ScrubTyVars.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// ScrubTyVars.cc -- 
+// ScrubTyVars.cc --
 //
 // Author           : Richard C. Bilson
@@ -28,5 +28,5 @@
 		TyVarMap::const_iterator tyVar = tyVars.find( typeInst->get_name() );
 		if ( tyVar != tyVars.end() ) {
-			switch ( tyVar->second ) {
+			switch ( tyVar->second.kind ) {
 			  case TypeDecl::Any:
 			  case TypeDecl::Dtype:
@@ -52,5 +52,5 @@
 		return ty;
 	}
-	
+
 	Type * ScrubTyVars::mutate( StructInstType *structInst ) {
 		return mutateAggregateType( structInst );
Index: src/ResolvExpr/AdjustExprType.cc
===================================================================
--- src/ResolvExpr/AdjustExprType.cc	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ 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 186fd864a423a60033d1f353374a4070a3294b18)
+++ 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 186fd864a423a60033d1f353374a4070a3294b18)
+++ 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 186fd864a423a60033d1f353374a4070a3294b18)
+++ 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 186fd864a423a60033d1f353374a4070a3294b18)
+++ 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 186fd864a423a60033d1f353374a4070a3294b18)
+++ 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 186fd864a423a60033d1f353374a4070a3294b18)
+++ 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 186fd864a423a60033d1f353374a4070a3294b18)
+++ 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
Index: src/SymTab/Validate.cc
===================================================================
--- src/SymTab/Validate.cc	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/SymTab/Validate.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -361,4 +361,16 @@
 	void Pass2::visit( TraitInstType *contextInst ) {
 		Parent::visit( contextInst );
+		if ( contextInst->get_name() == "sized" ) {
+			// "sized" is a special trait with no members - just flick the sized status on for the type variable
+			if ( contextInst->get_parameters().size() != 1 ) {
+				throw SemanticError( "incorrect number of context parameters: ", contextInst );
+			}
+			TypeExpr * param = safe_dynamic_cast< TypeExpr * > ( contextInst->get_parameters().front() );
+			TypeInstType * inst = safe_dynamic_cast< TypeInstType * > ( param->get_type() );
+			TypeDecl * decl = inst->get_baseType();
+			decl->set_sized( true );
+			// since "sized" is special, the next few steps don't apply
+			return;
+		}
 		TraitDecl *ctx = indexer->lookupTrait( contextInst->get_name() );
 		if ( ! ctx ) {
@@ -584,5 +596,5 @@
 
 		typedeclNames[ typeDecl->get_name() ] = typeDecl;
-		return typeDecl;
+		return Mutator::mutate( typeDecl );
 	}
 
Index: src/SynTree/ApplicationExpr.cc
===================================================================
--- src/SynTree/ApplicationExpr.cc	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/SynTree/ApplicationExpr.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -30,4 +30,5 @@
 	if ( &other == this ) return *this;
 	decl = other.decl;
+	// xxx - this looks like a memory leak
 	actualType = maybeClone( other.actualType );
 	formalType = maybeClone( other.formalType );
Index: src/SynTree/Declaration.h
===================================================================
--- src/SynTree/Declaration.h	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/SynTree/Declaration.h	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -176,4 +176,14 @@
   public:
 	enum Kind { Any, Dtype, Ftype };
+	/// Data extracted from a type decl
+	struct Data {
+		TypeDecl::Kind kind;
+		bool isComplete;
+		Data() : kind( (TypeDecl::Kind)-1 ), isComplete( false ) {}
+		Data( TypeDecl * typeDecl ) : Data( typeDecl->get_kind(), typeDecl->isComplete() ) {}
+		Data( Kind kind, bool isComplete ) : kind( kind ), isComplete( isComplete ) {}
+		bool operator==(const Data & other) const { return kind == other.kind && isComplete == other.isComplete; }
+		bool operator!=(const Data & other) const { return !(*this == other);}
+	};
 
 	TypeDecl( const std::string &name, DeclarationNode::StorageClass sc, Type *type, Kind kind );
@@ -182,4 +192,8 @@
 	Kind get_kind() const { return kind; }
 
+	bool isComplete() const { return kind == Any || sized; }
+	bool get_sized() const { return sized; }
+	TypeDecl * set_sized( bool newValue ) { sized = newValue; return this; }
+
 	virtual TypeDecl *clone() const { return new TypeDecl( *this ); }
 	virtual void accept( Visitor &v ) { v.visit( this ); }
@@ -188,4 +202,5 @@
 	virtual std::string typeString() const;
 	Kind kind;
+	bool sized;
 };
 
@@ -280,4 +295,5 @@
 
 std::ostream & operator<<( std::ostream & out, const Declaration * decl );
+std::ostream & operator<<( std::ostream & os, const TypeDecl::Data & data );
 
 #endif // DECLARATION_H
Index: src/SynTree/ReferenceToType.cc
===================================================================
--- src/SynTree/ReferenceToType.cc	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/SynTree/ReferenceToType.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -128,4 +128,8 @@
 }
 
+TypeInstType::TypeInstType( const TypeInstType &other ) : Parent( other ), baseType( other.baseType ), isFtype( other.isFtype ) {
+}
+
+
 TypeInstType::~TypeInstType() {
 	// delete baseType; //This is shared and should not be deleted
Index: src/SynTree/Type.h
===================================================================
--- src/SynTree/Type.h	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/SynTree/Type.h	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -337,5 +337,5 @@
 	TypeInstType( const Type::Qualifiers &tq, const std::string &name, TypeDecl *baseType );
 	TypeInstType( const Type::Qualifiers &tq, const std::string &name, bool isFtype );
-	TypeInstType( const TypeInstType &other ) : Parent( other ), baseType( other.baseType ), isFtype( other.isFtype ) {}
+	TypeInstType( const TypeInstType &other );
 	~TypeInstType();
 
Index: src/SynTree/TypeDecl.cc
===================================================================
--- src/SynTree/TypeDecl.cc	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/SynTree/TypeDecl.cc	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// TypeDecl.cc -- 
+// TypeDecl.cc --
 //
 // Author           : Richard C. Bilson
@@ -18,8 +18,8 @@
 #include "Common/utility.h"
 
-TypeDecl::TypeDecl( const std::string &name, DeclarationNode::StorageClass sc, Type *type, Kind kind ) : Parent( name, sc, type ), kind( kind ) {
+TypeDecl::TypeDecl( const std::string &name, DeclarationNode::StorageClass sc, Type *type, Kind kind ) : Parent( name, sc, type ), kind( kind ), sized( kind == Any ) {
 }
 
-TypeDecl::TypeDecl( const TypeDecl &other ) : Parent( other ), kind( other.kind ) {
+TypeDecl::TypeDecl( const TypeDecl &other ) : Parent( other ), kind( other.kind ), sized( other.sized ) {
 }
 
@@ -29,4 +29,8 @@
 }
 
+std::ostream & operator<<( std::ostream & os, const TypeDecl::Data & data ) {
+  return os << data.kind << ", " << data.isComplete;
+}
+
 // Local Variables: //
 // tab-width: 4 //
Index: src/libcfa/prelude.cf
===================================================================
--- src/libcfa/prelude.cf	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/libcfa/prelude.cf	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -108,8 +108,8 @@
 forall( otype T ) const volatile T *	 --?( const volatile T ** );
 
-forall( otype T ) lvalue T		 *?(		     T * );
-forall( otype T ) const lvalue T		 *?( const	     T * );
-forall( otype T ) volatile lvalue T	 *?(       volatile  T * );
-forall( otype T ) const volatile lvalue T *?( const volatile  T * );
+forall( dtype T | sized(T) ) lvalue T		 *?(		     T * );
+forall( dtype T | sized(T) ) const lvalue T		 *?( const	     T * );
+forall( dtype T | sized(T) ) volatile lvalue T	 *?(       volatile  T * );
+forall( dtype T | sized(T) ) const volatile lvalue T *?( const volatile  T * );
 forall( ftype FT ) lvalue FT		 *?( FT * );
 
Index: src/tests/.expect/completeTypeError.txt
===================================================================
--- src/tests/.expect/completeTypeError.txt	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
+++ src/tests/.expect/completeTypeError.txt	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -0,0 +1,32 @@
+CFA Version 1.0.0 (debug)
+Error: No reasonable alternatives for expression Applying untyped: 
+  Name: baz
+...to: 
+  Name: v
+
+
+Error: No reasonable alternatives for expression Applying untyped: 
+  Name: quux
+...to: 
+  Name: v
+
+
+Error: No reasonable alternatives for expression Applying untyped: 
+  Name: baz
+...to: 
+  Name: y
+
+
+Error: No reasonable alternatives for expression Applying untyped: 
+  Name: quux
+...to: 
+  Name: y
+
+
+Error: No reasonable alternatives for expression Applying untyped: 
+  Name: baz
+...to: 
+  Name: z
+
+
+make: *** [completeTypeError] Error 1
Index: src/tests/Makefile.am
===================================================================
--- src/tests/Makefile.am	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/tests/Makefile.am	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -65,2 +65,4 @@
 	${CC} ${CFALGS} -DERR1 ${<} -o ${@}
 
+completeTypeError : completeTypeError.c
+	${CC} ${CFALGS} -DERR1 ${<} -o ${@}
Index: src/tests/Makefile.in
===================================================================
--- src/tests/Makefile.in	(revision 186fd864a423a60033d1f353374a4070a3294b18)
+++ src/tests/Makefile.in	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -673,4 +673,7 @@
 	${CC} ${CFALGS} -DERR1 ${<} -o ${@}
 
+completeTypeError : completeTypeError.c
+	${CC} ${CFALGS} -DERR1 ${<} -o ${@}
+
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
Index: src/tests/completeTypeError.c
===================================================================
--- src/tests/completeTypeError.c	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
+++ src/tests/completeTypeError.c	(revision 2c57025b9dec0a2c5dae7c32ad6e1ac3ccf8f76e)
@@ -0,0 +1,57 @@
+void foo(int *) {}
+void bar(void *) {}
+forall(otype T) void baz(T *);
+forall(dtype T) void qux(T *);
+forall(dtype T | sized(T)) void quux(T *);
+
+int main() {
+	int *i;
+	void *v;
+
+	// okay
+	foo(i);
+	bar(i);
+	baz(i);
+	qux(i);
+	quux(i);
+
+	bar(v);
+	qux(v);
+	foo(v); // questionable, but works at the moment for C compatibility
+
+	// bad
+	baz(v);
+	quux(v);
+}
+
+forall(otype T)
+void baz(T * x) {
+	// okay
+	bar(x);
+	baz(x);
+	qux(x);
+	quux(x);
+}
+
+forall(dtype T)
+void qux(T * y) {
+	// okay
+	bar(y);
+	qux(y);
+
+	// bad
+	baz(y);
+	quux(y);
+}
+
+forall(dtype T | sized(T))
+void qux(T * z) {
+	// okay
+	bar(z);
+	qux(z);
+	quux(z);
+
+	// bad
+	baz(z);
+}
+
