Index: src/AST/Expr.cpp
===================================================================
--- src/AST/Expr.cpp	(revision 4ae2364c91966b9356cb3931775e9f136d7de727)
+++ src/AST/Expr.cpp	(revision ee574a219a52c0b47523b1a790d363cc61f813d3)
@@ -65,5 +65,5 @@
 			// base type
 			ret->result = base;
-			add_lvalue( ret->result );
+			add_qualifiers( ret->result, CV::Lvalue );
 		}
 	}
@@ -165,5 +165,5 @@
 	genericSubsitution( aggregate->result ).apply( result );
 	// ensure lvalue and appropriate restrictions from aggregate type
-	result.get_and_mutate()->qualifiers |= aggregate->result->qualifiers | CV::Lvalue;
+	add_qualifiers( result, aggregate->result->qualifiers | CV::Lvalue );
 }
 
@@ -175,5 +175,5 @@
 	assert( var->get_type() );
 	result = var->get_type();
-	add_lvalue( result );
+	add_qualifiers( result, CV::Lvalue );
 }
 
@@ -309,5 +309,5 @@
 	assert( t && i );
 	result = t;
-	add_lvalue( result );
+	add_qualifiers( result, CV::Lvalue );
 }
 
@@ -326,5 +326,5 @@
 	// like MemberExpr, TupleIndexExpr is always an lvalue
 	result = type->types[ index ];
-	add_lvalue( result );
+	add_qualifiers( result, CV::Lvalue );
 }
 
Index: src/AST/Node.hpp
===================================================================
--- src/AST/Node.hpp	(revision 4ae2364c91966b9356cb3931775e9f136d7de727)
+++ src/AST/Node.hpp	(revision ee574a219a52c0b47523b1a790d363cc61f813d3)
@@ -166,4 +166,8 @@
 	const o_node_t * as() const { return dynamic_cast<const o_node_t *>(node); }
 
+	/// wrapper for convenient access to strict_dynamic_cast
+	template<typename o_node_t>
+	const o_node_t * strict_as() const { return strict_dynamic_cast<const o_node_t *>(node); }
+
 	/// Returns a mutable version of the pointer in this node.
 	node_t * get_and_mutate();
Index: src/AST/Type.hpp
===================================================================
--- src/AST/Type.hpp	(revision 4ae2364c91966b9356cb3931775e9f136d7de727)
+++ src/AST/Type.hpp	(revision ee574a219a52c0b47523b1a790d363cc61f813d3)
@@ -77,14 +77,20 @@
 };
 
-/// Set the `is_lvalue` qualifier on this type, cloning only if necessary
+/// Clear/reset the qualifiers on this type, cloning only if necessary
 template< enum Node::ref_type ref_t >
-void add_lvalue( ptr_base< Type, ref_t > & p ) {
-	if ( ! p->qualifiers.is_lvalue ) p.get_and_mutate()->qualifiers.is_lvalue = true;
+void reset_qualifiers( ptr_base< Type, ref_t > & p, CV::Qualifiers q = {} ) {
+	if ( p->qualifiers.val != q.val ) p.get_and_mutate()->qualifiers = q;
 }
 
-/// Clear the qualifiers on this type, cloning only if necessary
+/// Add the specified qualifiers to this type, cloning only if necessary
 template< enum Node::ref_type ref_t >
-void clear_qualifiers( ptr_base< Type, ref_t > & p ) {
-	if ( p->qualifiers != CV::Qualifiers{} ) p.get_and_mutate()->qualifiers = CV::Qualifiers{};
+void add_qualifiers( ptr_base< Type, ref_t > & p, CV::Qualifiers q ) {
+	if ( ( p->qualifiers.val & q.val ) != q.val ) p.get_and_mutate()->qualifiers |= q;
+}
+
+/// Remove the specified qualifiers from this type, cloning only if necessary
+template< enum Node::ref_type ref_t >
+void remove_qualifiers( ptr_base< Type, ref_t > & p, CV::Qualifiers q ) {
+	if ( ( p->qualifiers.val & q.val ) != 0 ) p.get_and_mutate()->qualifiers -= q;
 }
 
Index: src/AST/TypeEnvironment.cpp
===================================================================
--- src/AST/TypeEnvironment.cpp	(revision 4ae2364c91966b9356cb3931775e9f136d7de727)
+++ src/AST/TypeEnvironment.cpp	(revision ee574a219a52c0b47523b1a790d363cc61f813d3)
@@ -235,14 +235,14 @@
 }
 
+/// true if a type is a function type
+bool isFtype( const Type * type ) {
+	if ( dynamic_cast< const FunctionType * >( type ) ) {
+		return true;
+	} else if ( auto typeInst = dynamic_cast< const TypeInstType * >( type ) ) {
+		return typeInst->kind == TypeVar::Ftype;
+	} else return false;
+}
+
 namespace {
-	/// true if a type is a function type
-	bool isFtype( const Type * type ) {
-		if ( dynamic_cast< const FunctionType * >( type ) ) {
-			return true;
-		} else if ( auto typeInst = dynamic_cast< const TypeInstType * >( type ) ) {
-			return typeInst->kind == TypeVar::Ftype;
-		} else return false;
-	}
-
 	/// true if the given type can be bound to the given type variable
 	bool tyVarCompatible( const TypeDecl::Data & data, const Type * type ) {
@@ -285,5 +285,5 @@
 			ptr<Type> common;
 			ptr<Type> newType = it->bound;
-			newType.get_and_mutate()->qualifiers = typeInst->qualifiers;
+			reset_qualifiers( newType, typeInst->qualifiers );
 			if ( unifyInexact( 
 					newType, target, *this, need, have, open, 
@@ -291,10 +291,10 @@
 				if ( common ) {
 					it->bound = std::move(common);
-					clear_qualifiers( it->bound );
+					reset_qualifiers( it->bound );
 				}
 			} else return false;
 		} else {
 			it->bound = std::move(target);
-			clear_qualifiers( it->bound );
+			reset_qualifiers( it->bound );
 			it->allowWidening = widen.first && widen.second;
 		}
@@ -351,5 +351,5 @@
 			if ( common ) {
 				c1->bound = std::move(common);
-				clear_qualifiers( c1->bound );
+				reset_qualifiers( c1->bound );
 			}
 			c1->data.isComplete |= data.isComplete;
@@ -411,5 +411,5 @@
 				if ( common ) {
 					to.bound = std::move(common);
-					clear_qualifiers( to.bound );
+					reset_qualifiers( to.bound );
 				}
 			} else return false; // cannot unify
Index: src/AST/TypeEnvironment.hpp
===================================================================
--- src/AST/TypeEnvironment.hpp	(revision 4ae2364c91966b9356cb3931775e9f136d7de727)
+++ src/AST/TypeEnvironment.hpp	(revision ee574a219a52c0b47523b1a790d363cc61f813d3)
@@ -112,5 +112,5 @@
 	EqvClass( const std::string & v, const Type * b, bool w, const TypeDecl::Data & d )
 	: vars{ v }, bound( b ), allowWidening( w ), data( d ) {
-		clear_qualifiers( bound );
+		reset_qualifiers( bound );
 	}
 
