Index: src/SymTab/Validate.cc
===================================================================
--- src/SymTab/Validate.cc	(revision 69918ceafe1d4bbe9af9eaea89bea8729f968b22)
+++ src/SymTab/Validate.cc	(revision a12c81f30d4d4bc145747889f469cda8edb98b21)
@@ -88,4 +88,8 @@
 	};
 
+	struct FixQualifiedTypes final : public WithIndexer {
+		Type * postmutate( QualifiedType * );
+	};
+
 	struct HoistStruct final : public WithDeclsToAdd, public WithGuards {
 		/// Flattens nested struct types
@@ -282,4 +286,5 @@
 		PassVisitor<LabelAddressFixer> labelAddrFixer;
 		PassVisitor<HoistTypeDecls> hoistDecls;
+		PassVisitor<FixQualifiedTypes> fixQual;
 
 		acceptAll( translationUnit, hoistDecls );
@@ -288,4 +293,5 @@
 		acceptAll( translationUnit, epc ); // must happen before VerifyCtorDtorAssign, because void return objects should not exist; before LinkReferenceToTypes because it is an indexer and needs correct types for mangling
 		acceptAll( translationUnit, lrt ); // must happen before autogen, because sized flag needs to propagate to generated functions
+		mutateAll( translationUnit, fixQual ); // must happen after LinkReferenceToTypes, because aggregate members are accessed
 		HoistStruct::hoistStruct( translationUnit ); // must happen after EliminateTypedef, so that aggregate typedefs occur in the correct order
 		EliminateTypedef::eliminateTypedef( translationUnit ); //
@@ -341,4 +347,80 @@
 	void HoistTypeDecls::previsit( UntypedOffsetofExpr * expr ) {
 		handleType( expr->type );
+	}
+
+
+	Type * FixQualifiedTypes::postmutate( QualifiedType * qualType ) {
+		// TODO: change asserts to SemanticErrors as necessary
+		Type * parent = qualType->parent;
+		Type * child = qualType->child;
+		if ( dynamic_cast< GlobalScopeType * >( qualType->parent ) ) {
+			// .T => lookup T at global scope
+			if ( StructInstType * inst = dynamic_cast< StructInstType * >( child ) ) {
+				auto aggr = indexer.globalLookupStruct( inst->name );
+				return new StructInstType( qualType->get_qualifiers(), aggr );
+			} else if ( UnionInstType * inst = dynamic_cast< UnionInstType * >( child ) ) {
+				auto aggr =  indexer.globalLookupUnion( inst->name );
+				return new UnionInstType( qualType->get_qualifiers(), aggr );
+			} else if ( EnumInstType * inst = dynamic_cast< EnumInstType * >( child ) ) {
+				auto aggr = indexer.globalLookupEnum( inst->name );
+				return new EnumInstType( qualType->get_qualifiers(), aggr );
+			} else if ( TypeInstType * inst = dynamic_cast< TypeInstType * >( child ) ) {
+				auto td = indexer.globalLookupType( inst->name );
+				assertf( td, "did not find type at global scope with name: %s", inst->name.c_str() );
+				auto base = td->base;
+				if ( base ) return td->base->clone();
+				assert( false );
+			} else {
+				// .T => T is not a SUE type name
+				assert( false );
+			}
+		} else {
+			// S.T => S must be an aggregate type, find the declaration for T in S.
+			AggregateDecl * aggr = nullptr;
+			if ( StructInstType * inst = dynamic_cast< StructInstType * >( parent ) ) {
+				aggr = inst->baseStruct;
+			} else if ( UnionInstType * inst = dynamic_cast< UnionInstType * > ( parent ) ) {
+				aggr = inst->baseUnion;
+			} else {
+				assert( false );
+			}
+			assert( aggr ); // TODO: need to handle forward declarations
+			for ( Declaration * member : aggr->members ) {
+				if ( StructInstType * inst = dynamic_cast< StructInstType * >( child ) ) {
+					if ( StructDecl * aggr = dynamic_cast< StructDecl * >( member ) ) {
+						if ( aggr->name == inst->name ) {
+							return new StructInstType( qualType->get_qualifiers(), aggr );
+						}
+					}
+				} else if ( UnionInstType * inst = dynamic_cast< UnionInstType * >( child ) ) {
+					if ( UnionDecl * aggr = dynamic_cast< UnionDecl * > ( member ) ) {
+						if ( aggr->name == inst->name ) {
+							return new UnionInstType( qualType->get_qualifiers(), aggr );
+						}
+					}
+				} else if ( EnumInstType * inst = dynamic_cast< EnumInstType * >( child ) ) {
+					if ( EnumDecl * aggr = dynamic_cast< EnumDecl * > ( member ) ) {
+						if ( aggr->name == inst->name ) {
+							return new EnumInstType( qualType->get_qualifiers(), aggr );
+						}
+					}
+				} else if ( TypeInstType * inst = dynamic_cast< TypeInstType * >( child ) ) {
+					// struct typedefs are being replaced by forward decls too early; move it to hoist struct
+					if ( NamedTypeDecl * aggr = dynamic_cast< NamedTypeDecl * > ( member ) ) {
+						if ( aggr->name == inst->name ) {
+							if ( aggr->base ) return aggr->base->clone();
+							assert( false );
+						}
+					}
+				} else {
+					// S.T - S is not an aggregate => error
+					assertf( false, "unhandled qualified child type: %s", toCString(qualType) );
+				}
+			}
+			// failed to find a satisfying definition of type
+			assertf( false, "failed to find a satisfying definition of %s in %s", toCString(child), toCString(parent) );
+		}
+
+		// ... may want to link canonical SUE definition to each forward decl so that it becomes easier to lookup?
 	}
 
