Index: src/AST/Print.cpp
===================================================================
--- src/AST/Print.cpp	(revision 3ca912abaa01892790ebf2b72b458acd0b60d64d)
+++ src/AST/Print.cpp	(revision 68c9165ba8977f22fd2ed8d7175c671e03359a1a)
@@ -99,4 +99,44 @@
 	}
 
+	void print( const ast::ParameterizedType::ForallList & forall ) {
+		if ( forall.empty() ) return;	
+		os << "forall" << std::endl;
+		++indent;
+		printAll( forall );
+		os << indent;
+		--indent;
+	}
+
+	void print( const std::vector<ptr<Attribute>> & attrs ) {
+		if ( attrs.empty() ) return;
+		os << "with attributes" << std::endl;
+		++indent;
+		printAll( attrs );
+		--indent;
+	}
+
+	void print( const std::vector<ptr<Expr>> & params ) {
+		if ( params.empty() ) return;
+		os << std::endl << indent << "... with parameters" << std::endl;
+		++indent;
+		printAll( params );
+		--indent;
+	}
+
+	void preprint( const ast::Type * node ) {
+		print( node->qualifiers );
+	}
+
+	void preprint( const ast::ParameterizedType * node ) {
+		print( node->forall );
+		print( node->qualifiers );
+	}
+
+	void preprint( const ast::ReferenceToType * node ) {
+		print( node->forall );
+		print( node->attributes );
+		print( node->qualifiers );
+	}
+	
 	void print( const ast::AggregateDecl * node ) {
 		os << node->typeString() << " " << node->name << ":";
@@ -566,72 +606,227 @@
 
 	virtual const ast::Type * visit( const ast::VoidType * node ) {
+		preprint( node );
+		os << "void";
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::BasicType * node ) {
+		preprint( node );
+		os << ast::BasicType::typeNames[ node->kind ];
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::PointerType * node ) {
+		preprint( node );
+		if ( ! node->isArray() ) {
+			os << "pointer to ";
+		} else {
+			os << "decayed ";
+			if ( node->isStatic ) {
+				os << "static ";
+			}
+
+			if ( node->isVarLen ) {
+				os << "variable length array of ";
+			} else if ( node->dimension ) {
+				os << "array of ";
+				node->dimension->accept( *this );
+				os << " ";
+			}
+		}
+
+		if ( node->base ) {
+			node->base->accept( *this );
+		} else {
+			os << "UNDEFINED";
+		}
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::ArrayType * node ) {
+		preprint( node );
+		if ( node->isStatic ) {
+			os << "static ";
+		}
+
+		if ( node->isVarLen ) {
+			os << "variable length array of ";
+		} else if ( node->dimension ) {
+			os << "array of ";
+		} else {
+			os << "open array of ";
+		}
+
+		if ( node->base ) {
+			node->base->accept( *this );
+		} else {
+			os << "UNDEFINED";
+		}
+
+		if ( node->dimension ) {
+			os << " with dimension of ";
+			node->dimension->accept( *this );
+		}
+
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::ReferenceType * node ) {
+		preprint( node );
+
+		os << "reference to ";
+		if ( node->base ) {
+			node->base->accept( *this );
+		} else {
+			os << "UNDEFINED";
+		}
+
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::QualifiedType * node ) {
+		preprint( node );
+
+		++indent;
+		os << "Qualified Type:" << std::endl << indent;
+		node->parent->accept( *this );
+		os << std::endl << indent;
+		node->child->accept( *this );
+		os << std::endl;
+		--indent;
+
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::FunctionType * node ) {
+		preprint( node );
+
+		os << "function" << std::endl;
+		if ( ! node->params.empty() ) {
+			os << indent << "... with parameters" << std::endl;
+			++indent;
+			printAll( node->params );
+			if ( node->isVarArgs ) {
+				os << indent << "and a variable number of other arguments" << std::endl;
+			}
+			--indent;
+		} else if ( node->isVarArgs ) {
+			os << indent+1 << "accepting unspecified arguments" << std::endl;
+		}
+
+		os << indent << "... returning";
+		if ( node->returns.empty() ) {
+			os << " nothing" << std::endl;
+		} else {
+			os << std::endl;
+			++indent;
+			printAll( node->returns );
+			--indent;
+		}
+
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::StructInstType * node ) {
+		preprint( node );
+
+		os << "instance of struct " << node->name;
+		if ( node->base ) {
+			os << " " << ( node->base->body ? "with" : "without" ) << " body";
+		}
+		print( node->params );
+
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::UnionInstType * node ) {
+		preprint( node );
+
+		os << "instance of union " << node->name;
+		if ( node->base ) {
+			os << " " << ( node->base->body ? "with" : "without" ) << " body";
+		}
+		print( node->params );
+
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::EnumInstType * node ) {
+		preprint( node );
+
+		os << "instance of enum " << node->name;
+		if ( node->base ) {
+			os << " " << ( node->base->body ? "with" : "without" ) << " body";
+		}
+		print( node->params );
+
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::TraitInstType * node ) {
+		preprint( node );
+
+		os << "instance of trait " << node->name;
+		print( node->params );
+
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::TypeInstType * node ) {
+		preprint( node );
+
+		os << "instance of type " << node->name 
+		   << " (" << (node->kind == ast::TypeVar::Ftype ? "" : "not ") << "function type)";
+		print( node->params );
+
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::TupleType * node ) {
+		preprint( node );
+
+		os << "tuple of types" << std::endl;
+		++indent;
+		printAll( node->types );
+		--indent;
+
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::TypeofType * node ) {
+		preprint( node );
+
+		if ( node->kind == ast::TypeofType::Basetypeof ) { os << "base-"; }
+		os << "type-of expression ";
+		if ( node->expr ) {
+			node->expr->accept( *this );
+		} else {
+			os << "UNDEFINED";
+		}
+
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::VarArgsType * node ) {
+		preprint( node );
+		os << "builtin var args pack";
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::ZeroType * node ) {
+		preprint( node );
+		os << "zero_t";
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::OneType * node ) {
+		preprint( node );
+		os << "one_t";
 		return node;
 	}
 
 	virtual const ast::Type * visit( const ast::GlobalScopeType * node ) {
+		preprint( node );
+		os << "Global Scope Type";
 		return node;
 	}
Index: src/AST/Type.hpp
===================================================================
--- src/AST/Type.hpp	(revision 3ca912abaa01892790ebf2b72b458acd0b60d64d)
+++ src/AST/Type.hpp	(revision 68c9165ba8977f22fd2ed8d7175c671e03359a1a)
@@ -308,8 +308,4 @@
 	virtual ReferenceToType * clone() const override = 0;
 	MUTATE_FRIEND
-
-protected:
-	/// Name for the kind of type this is
-	virtual std::string typeString() const = 0;
 };
 
@@ -333,6 +329,4 @@
 	StructInstType * clone() const override { return new StructInstType{ *this }; }
 	MUTATE_FRIEND
-
-	std::string typeString() const override { return "struct"; }
 };
 
@@ -356,6 +350,4 @@
 	UnionInstType * clone() const override { return new UnionInstType{ *this }; }
 	MUTATE_FRIEND
-
-	std::string typeString() const override { return "union"; }
 };
 
@@ -379,6 +371,4 @@
 	EnumInstType * clone() const override { return new EnumInstType{ *this }; }
 	MUTATE_FRIEND
-
-	std::string typeString() const override { return "enum"; }
 };
 
@@ -403,6 +393,4 @@
 	TraitInstType * clone() const override { return new TraitInstType{ *this }; }
 	MUTATE_FRIEND
-
-	std::string typeString() const override { return "trait"; }
 };
 
@@ -432,6 +420,4 @@
 	TypeInstType * clone() const override { return new TypeInstType{ *this }; }
 	MUTATE_FRIEND
-
-	std::string typeString() const override { return "type"; }
 };
 
