Index: src/AST/Attribute.hpp
===================================================================
--- src/AST/Attribute.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/Attribute.hpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -27,4 +27,5 @@
 class Expr;
 
+/// An entry in an attribute list: `__attribute__(( ... ))`
 class Attribute final : public Node {
 public:
Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/Convert.cpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -559,5 +559,5 @@
 		auto stmt = new SuspendStmt();
 		stmt->then   = get<CompoundStmt>().accept1( node->then   );
-		switch(node->type) {
+		switch (node->kind) {
 			case ast::SuspendStmt::None     : stmt->type = SuspendStmt::None     ; break;
 			case ast::SuspendStmt::Coroutine: stmt->type = SuspendStmt::Coroutine; break;
@@ -1695,5 +1695,5 @@
 			GET_ACCEPT_V(attributes, Attribute),
 			{ old->get_funcSpec().val },
-			old->type->isVarArgs
+			(old->type->isVarArgs) ? ast::VariableArgs : ast::FixedArgs
 		};
 
@@ -2001,5 +2001,5 @@
 			GET_ACCEPT_1(else_, Stmt),
 			GET_ACCEPT_V(initialization, Stmt),
-			old->isDoWhile,
+			(old->isDoWhile) ? ast::DoWhile : ast::While,
 			GET_LABELS_V(old->labels)
 		);
@@ -2143,5 +2143,5 @@
 	virtual void visit( const SuspendStmt * old ) override final {
 		if ( inCache( old ) ) return;
-		ast::SuspendStmt::Type type;
+		ast::SuspendStmt::Kind type;
 		switch (old->type) {
 			case SuspendStmt::Coroutine: type = ast::SuspendStmt::Coroutine; break;
Index: src/AST/Decl.cpp
===================================================================
--- src/AST/Decl.cpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/Decl.cpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -57,9 +57,9 @@
 	std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,
 	CompoundStmt * stmts, Storage::Classes storage, Linkage::Spec linkage,
-	std::vector<ptr<Attribute>>&& attrs, Function::Specs fs, bool isVarArgs)
+	std::vector<ptr<Attribute>>&& attrs, Function::Specs fs, ArgumentFlag isVarArgs )
 : DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ),
 	type_params(std::move(forall)), assertions(),
 	params(std::move(params)), returns(std::move(returns)), stmts( stmts ) {
-	FunctionType * ftype = new FunctionType(static_cast<ArgumentFlag>(isVarArgs));
+	FunctionType * ftype = new FunctionType( isVarArgs );
 	for (auto & param : this->params) {
 		ftype->params.emplace_back(param->get_type());
@@ -81,10 +81,10 @@
 	std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,
 	CompoundStmt * stmts, Storage::Classes storage, Linkage::Spec linkage,
-	std::vector<ptr<Attribute>>&& attrs, Function::Specs fs, bool isVarArgs)
+	std::vector<ptr<Attribute>>&& attrs, Function::Specs fs, ArgumentFlag isVarArgs )
 : DeclWithType( location, name, storage, linkage, std::move(attrs), fs ),
 		type_params( std::move( forall) ), assertions( std::move( assertions ) ),
 		params( std::move(params) ), returns( std::move(returns) ),
 		type( nullptr ), stmts( stmts ) {
-	FunctionType * type = new FunctionType( (isVarArgs) ? VariableArgs : FixedArgs );
+	FunctionType * type = new FunctionType( isVarArgs );
 	for ( auto & param : this->params ) {
 		type->params.emplace_back( param->get_type() );
Index: src/AST/Decl.hpp
===================================================================
--- src/AST/Decl.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/Decl.hpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -10,6 +10,6 @@
 // Created On       : Thu May 9 10:00:00 2019
 // Last Modified By : Andrew Beach
-// Last Modified On : Thu Nov 24  9:44:00 2022
-// Update Count     : 34
+// Last Modified On : Wed Apr  5 10:42:00 2023
+// Update Count     : 35
 //
 
@@ -122,4 +122,7 @@
 };
 
+/// Function variable arguments flag
+enum ArgumentFlag { FixedArgs, VariableArgs };
+
 /// Object declaration `int foo()`
 class FunctionDecl : public DeclWithType {
@@ -144,5 +147,5 @@
 		std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,
 		CompoundStmt * stmts, Storage::Classes storage = {}, Linkage::Spec linkage = Linkage::Cforall,
-		std::vector<ptr<Attribute>>&& attrs = {}, Function::Specs fs = {}, bool isVarArgs = false);
+		std::vector<ptr<Attribute>>&& attrs = {}, Function::Specs fs = {}, ArgumentFlag isVarArgs = FixedArgs );
 
 	FunctionDecl( const CodeLocation & location, const std::string & name,
@@ -150,5 +153,5 @@
 		std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,
 		CompoundStmt * stmts, Storage::Classes storage = {}, Linkage::Spec linkage = Linkage::Cforall,
-		std::vector<ptr<Attribute>>&& attrs = {}, Function::Specs fs = {}, bool isVarArgs = false);
+		std::vector<ptr<Attribute>>&& attrs = {}, Function::Specs fs = {}, ArgumentFlag isVarArgs = FixedArgs );
 
 	const Type * get_type() const override;
@@ -313,5 +316,5 @@
 public:
 	bool isTyped; // isTyped indicated if the enum has a declaration like:
-	// enum (type_optional) Name {...} 
+	// enum (type_optional) Name {...}
 	ptr<Type> base; // if isTyped == true && base.get() == nullptr, it is a "void" type enum
 	enum class EnumHiding { Visible, Hide } hide;
@@ -371,4 +374,5 @@
 };
 
+/// Assembly declaration: `asm ... ( "..." : ... )`
 class AsmDecl : public Decl {
 public:
Index: src/AST/Expr.hpp
===================================================================
--- src/AST/Expr.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/Expr.hpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -254,4 +254,5 @@
 };
 
+/// A name qualified by a namespace or type.
 class QualifiedNameExpr final : public Expr {
 public:
@@ -259,5 +260,5 @@
 	std::string name;
 
-	QualifiedNameExpr( const CodeLocation & loc, const Decl * d, const std::string & n ) 
+	QualifiedNameExpr( const CodeLocation & loc, const Decl * d, const std::string & n )
 	: Expr( loc ), type_decl( d ), name( n ) {}
 
@@ -621,4 +622,5 @@
 };
 
+/// A name that refers to a generic dimension parameter.
 class DimensionExpr final : public Expr {
 public:
@@ -910,5 +912,4 @@
 };
 
-
 }
 
Index: src/AST/Init.hpp
===================================================================
--- src/AST/Init.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/Init.hpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -117,5 +117,5 @@
 	ptr<Init> init;
 
-	ConstructorInit( 
+	ConstructorInit(
 		const CodeLocation & loc, const Stmt * ctor, const Stmt * dtor, const Init * init )
 	: Init( loc, MaybeConstruct ), ctor( ctor ), dtor( dtor ), init( init ) {}
Index: src/AST/Inspect.cpp
===================================================================
--- src/AST/Inspect.cpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/Inspect.cpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -10,6 +10,6 @@
 // Created On       : Fri Jun 24 13:16:31 2022
 // Last Modified By : Andrew Beach
-// Last Modified On : Mon Oct  3 11:04:00 2022
-// Update Count     : 3
+// Last Modified On : Fri Apr 14 15:09:00 2023
+// Update Count     : 4
 //
 
@@ -168,3 +168,7 @@
 }
 
+bool isUnnamedBitfield( const ast::ObjectDecl * obj ) {
+	return obj && obj->name.empty() && obj->bitfieldWidth;
+}
+
 } // namespace ast
Index: src/AST/Inspect.hpp
===================================================================
--- src/AST/Inspect.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/Inspect.hpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -10,6 +10,6 @@
 // Created On       : Fri Jun 24 13:16:31 2022
 // Last Modified By : Andrew Beach
-// Last Modified On : Thr Sep 22 13:44:00 2022
-// Update Count     : 2
+// Last Modified On : Fri Apr 14 15:09:00 2023
+// Update Count     : 3
 //
 
@@ -38,3 +38,6 @@
 const ApplicationExpr * isIntrinsicCallExpr( const Expr * expr );
 
+/// Returns true if obj's name is the empty string and it has a bitfield width.
+bool isUnnamedBitfield( const ObjectDecl * obj );
+
 }
Index: src/AST/Pass.impl.hpp
===================================================================
--- src/AST/Pass.impl.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/Pass.impl.hpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -2075,5 +2075,4 @@
 	if ( __visit_children() ) {
 		maybe_accept( node, &TupleType::types );
-		maybe_accept( node, &TupleType::members );
 	}
 
Index: src/AST/Print.cpp
===================================================================
--- src/AST/Print.cpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/Print.cpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -766,8 +766,8 @@
 	virtual const ast::Stmt * visit( const ast::SuspendStmt * node ) override final {
 		os << "Suspend Statement";
-		switch (node->type) {
-			case ast::SuspendStmt::None     : os << " with implicit target"; break;
-			case ast::SuspendStmt::Generator: os << " for generator"; break;
-			case ast::SuspendStmt::Coroutine: os << " for coroutine"; break;
+		switch (node->kind) {
+		case ast::SuspendStmt::None     : os << " with implicit target"; break;
+		case ast::SuspendStmt::Generator: os << " for generator"; break;
+		case ast::SuspendStmt::Coroutine: os << " for coroutine"; break;
 		}
 		os << endl;
Index: src/AST/Stmt.hpp
===================================================================
--- src/AST/Stmt.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/Stmt.hpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -10,6 +10,6 @@
 // Created On       : Wed May  8 13:00:00 2019
 // Last Modified By : Andrew Beach
-// Last Modified On : Wed Apr 20 14:34:00 2022
-// Update Count     : 36
+// Last Modified On : Wed Apr  5 10:34:00 2023
+// Update Count     : 37
 //
 
@@ -205,4 +205,7 @@
 };
 
+// A while loop or a do-while loop:
+enum WhileDoKind { While, DoWhile };
+
 // While loop: while (...) ... else ... or do ... while (...) else ...;
 class WhileDoStmt final : public Stmt {
@@ -212,12 +215,12 @@
 	ptr<Stmt> else_;
 	std::vector<ptr<Stmt>> inits;
-	bool isDoWhile;
+	WhileDoKind isDoWhile;
 
 	WhileDoStmt( const CodeLocation & loc, const Expr * cond, const Stmt * body,
-				 const std::vector<ptr<Stmt>> && inits, bool isDoWhile = false, const std::vector<Label> && labels = {} )
+				 const std::vector<ptr<Stmt>> && inits, WhileDoKind isDoWhile = While, const std::vector<Label> && labels = {} )
 		: Stmt(loc, std::move(labels)), cond(cond), body(body), else_(nullptr), inits(std::move(inits)), isDoWhile(isDoWhile) {}
 
 	WhileDoStmt( const CodeLocation & loc, const Expr * cond, const Stmt * body, const Stmt * else_,
-				 const std::vector<ptr<Stmt>> && inits, bool isDoWhile = false, const std::vector<Label> && labels = {} )
+				 const std::vector<ptr<Stmt>> && inits, WhileDoKind isDoWhile = While, const std::vector<Label> && labels = {} )
 		: Stmt(loc, std::move(labels)), cond(cond), body(body), else_(else_), inits(std::move(inits)), isDoWhile(isDoWhile) {}
 
@@ -364,8 +367,8 @@
   public:
 	ptr<CompoundStmt> then;
-	enum Type { None, Coroutine, Generator } type = None;
-
-	SuspendStmt( const CodeLocation & loc, const CompoundStmt * then, Type type, const std::vector<Label> && labels = {} )
-		: Stmt(loc, std::move(labels)), then(then), type(type) {}
+	enum Kind { None, Coroutine, Generator } kind = None;
+
+	SuspendStmt( const CodeLocation & loc, const CompoundStmt * then, Kind kind, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), then(then), kind(kind) {}
 
 	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
@@ -424,4 +427,5 @@
 };
 
+// Clause in a waitfor statement: waitfor (..., ...) ...
 class WaitForClause final : public WhenClause {
   public:
@@ -527,4 +531,5 @@
 	MUTATE_FRIEND
 };
+
 } // namespace ast
 
Index: src/AST/SymbolTable.cpp
===================================================================
--- src/AST/SymbolTable.cpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/SymbolTable.cpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -260,10 +260,10 @@
 void SymbolTable::addId( const DeclWithType * decl, const Expr * baseExpr ) {
 	// default handling of conflicts is to raise an error
-	addId( decl, OnConflict::error(), baseExpr, decl->isDeleted ? decl : nullptr );
+	addIdCommon( decl, OnConflict::error(), baseExpr, decl->isDeleted ? decl : nullptr );
 }
 
 void SymbolTable::addDeletedId( const DeclWithType * decl, const Decl * deleter ) {
 	// default handling of conflicts is to raise an error
-	addId( decl, OnConflict::error(), nullptr, deleter );
+	addIdCommon( decl, OnConflict::error(), nullptr, deleter );
 }
 
@@ -677,10 +677,10 @@
 }
 
-void SymbolTable::addId(
-		const DeclWithType * decl, SymbolTable::OnConflict handleConflicts, const Expr * baseExpr,
-		const Decl * deleter ) {
+void SymbolTable::addIdCommon(
+		const DeclWithType * decl, SymbolTable::OnConflict handleConflicts,
+		const Expr * baseExpr, const Decl * deleter ) {
 	SpecialFunctionKind kind = getSpecialFunctionKind(decl->name);
 	if (kind == NUMBER_OF_KINDS) { // not a special decl
-		addId(decl, decl->name, idTable, handleConflicts, baseExpr, deleter);
+		addIdToTable(decl, decl->name, idTable, handleConflicts, baseExpr, deleter);
 	}
 	else {
@@ -695,11 +695,12 @@
 			assertf(false, "special decl with non-function type");
 		}
-		addId(decl, key, specialFunctionTable[kind], handleConflicts, baseExpr, deleter);
-	}
-}
-
-void SymbolTable::addId(
-		const DeclWithType * decl, const std::string & lookupKey, IdTable::Ptr & table, SymbolTable::OnConflict handleConflicts, const Expr * baseExpr,
-		const Decl * deleter ) {
+		addIdToTable(decl, key, specialFunctionTable[kind], handleConflicts, baseExpr, deleter);
+	}
+}
+
+void SymbolTable::addIdToTable(
+		const DeclWithType * decl, const std::string & lookupKey,
+		IdTable::Ptr & table, SymbolTable::OnConflict handleConflicts,
+		const Expr * baseExpr, const Decl * deleter ) {
 	++*stats().add_calls;
 	const std::string &name = decl->name;
@@ -778,22 +779,22 @@
 void SymbolTable::addMembers(
 		const AggregateDecl * aggr, const Expr * expr, SymbolTable::OnConflict handleConflicts ) {
-	for ( const Decl * decl : aggr->members ) {
-		if ( auto dwt = dynamic_cast< const DeclWithType * >( decl ) ) {
-			addId( dwt, handleConflicts, expr );
-			if ( dwt->name == "" ) {
-				const Type * t = dwt->get_type()->stripReferences();
-				if ( auto rty = dynamic_cast<const BaseInstType *>( t ) ) {
-					if ( ! dynamic_cast<const StructInstType *>(rty)
-						&& ! dynamic_cast<const UnionInstType *>(rty) ) continue;
-					ResolvExpr::Cost cost = ResolvExpr::Cost::zero;
-					ast::ptr<ast::TypeSubstitution> tmp = expr->env;
-					expr = mutate_field(expr, &Expr::env, nullptr);
-					const Expr * base = ResolvExpr::referenceToRvalueConversion( expr, cost );
-					base = mutate_field(base, &Expr::env, tmp);
-
-					addMembers(
-						rty->aggr(), new MemberExpr{ base->location, dwt, base }, handleConflicts );
-				}
-			}
+	for ( const ptr<Decl> & decl : aggr->members ) {
+		auto dwt = decl.as<DeclWithType>();
+		if ( nullptr == dwt ) continue;
+		addIdCommon( dwt, handleConflicts, expr );
+		// Inline through unnamed struct/union members.
+		if ( "" != dwt->name ) continue;
+		const Type * t = dwt->get_type()->stripReferences();
+		if ( auto rty = dynamic_cast<const BaseInstType *>( t ) ) {
+			if ( ! dynamic_cast<const StructInstType *>(rty)
+				&& ! dynamic_cast<const UnionInstType *>(rty) ) continue;
+			ResolvExpr::Cost cost = ResolvExpr::Cost::zero;
+			ast::ptr<ast::TypeSubstitution> tmp = expr->env;
+			expr = mutate_field(expr, &Expr::env, nullptr);
+			const Expr * base = ResolvExpr::referenceToRvalueConversion( expr, cost );
+			base = mutate_field(base, &Expr::env, tmp);
+
+			addMembers(
+				rty->aggr(), new MemberExpr{ base->location, dwt, base }, handleConflicts );
 		}
 	}
Index: src/AST/SymbolTable.hpp
===================================================================
--- src/AST/SymbolTable.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/SymbolTable.hpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -192,13 +192,14 @@
 
 	/// common code for addId, addDeletedId, etc.
-	void addId(
-		const DeclWithType * decl, OnConflict handleConflicts, const Expr * baseExpr = nullptr,
-		const Decl * deleter = nullptr );
+	void addIdCommon(
+		const DeclWithType * decl, OnConflict handleConflicts,
+		const Expr * baseExpr = nullptr, const Decl * deleter = nullptr );
 
 	/// common code for addId when special decls are placed into separate tables
-	void addId(
-		const DeclWithType * decl, const std::string & lookupKey, IdTable::Ptr & idTable, OnConflict handleConflicts, 
+	void addIdToTable(
+		const DeclWithType * decl, const std::string & lookupKey,
+		IdTable::Ptr & idTable, OnConflict handleConflicts,
 		const Expr * baseExpr = nullptr, const Decl * deleter = nullptr);
-	
+
 	/// adds all of the members of the Aggregate (addWith helper)
 	void addMembers( const AggregateDecl * aggr, const Expr * expr, OnConflict handleConflicts );
Index: src/AST/Type.cpp
===================================================================
--- src/AST/Type.cpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/Type.cpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -10,6 +10,6 @@
 // Created On       : Mon May 13 15:00:00 2019
 // Last Modified By : Andrew Beach
-// Last Modified On : Thu Nov 24  9:49:00 2022
-// Update Count     : 6
+// Last Modified On : Thu Apr  6 15:59:00 2023
+// Update Count     : 7
 //
 
@@ -199,23 +199,5 @@
 
 TupleType::TupleType( std::vector<ptr<Type>> && ts, CV::Qualifiers q )
-: Type( q ), types( std::move(ts) ), members() {
-	// This constructor is awkward. `TupleType` needs to contain objects so that members can be
-	// named, but members without initializer nodes end up getting constructors, which breaks
-	// things. This happens because the object decls have to be visited so that their types are
-	// kept in sync with the types listed here. Ultimately, the types listed here should perhaps
-	// be eliminated and replaced with a list-view over members. The temporary solution is to
-	// make a `ListInit` with `maybeConstructed = false`, so when the object is visited it is not
-	// constructed. Potential better solutions include:
-	//   a) Separate `TupleType` from its declarations, into `TupleDecl` and `Tuple{Inst?}Type`,
-	//      similar to the aggregate types.
-	//   b) Separate initializer nodes better, e.g. add a `MaybeConstructed` node that is replaced
-	//      by `genInit`, rather than the current boolean flag.
-	members.reserve( types.size() );
-	for ( const Type * ty : types ) {
-		members.emplace_back( new ObjectDecl{
-			CodeLocation(), "", ty, new ListInit( CodeLocation(), {}, {}, NoConstruct ),
-			Storage::Classes{}, Linkage::Cforall } );
-	}
-}
+: Type( q ), types( std::move(ts) ) {}
 
 bool isUnboundType(const Type * type) {
Index: src/AST/Type.hpp
===================================================================
--- src/AST/Type.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/AST/Type.hpp	(revision 7bef8cf5c01bdb7a3ef2d5f4c309a1715e4ba4e9)
@@ -10,6 +10,6 @@
 // Created On       : Thu May 9 10:00:00 2019
 // Last Modified By : Andrew Beach
-// Last Modified On : Thu Nov 24  9:47:00 2022
-// Update Count     : 8
+// Last Modified On : Thu Apr  6 15:58:00 2023
+// Update Count     : 9
 //
 
@@ -265,7 +265,4 @@
 };
 
-/// Function variable arguments flag
-enum ArgumentFlag { FixedArgs, VariableArgs };
-
 /// Type of a function `[R1, R2](*)(P1, P2, P3)`
 class FunctionType final : public Type {
@@ -460,5 +457,4 @@
 public:
 	std::vector<ptr<Type>> types;
-	std::vector<ptr<Decl>> members;
 
 	TupleType( std::vector<ptr<Type>> && ts, CV::Qualifiers q = {} );
