Index: src/Parser/DeclarationNode.cc
===================================================================
--- src/Parser/DeclarationNode.cc	(revision aeec6b776d14e45c7d0eae9f5b5c1a1a6ab38bea)
+++ src/Parser/DeclarationNode.cc	(revision 3d7e53b5573c6a90196213b47ca833bd80f2f16e)
@@ -273,9 +273,8 @@
 
 DeclarationNode * DeclarationNode::newAggregate( Aggregate kind, const string * name, ExpressionNode * actuals, DeclarationNode * fields, bool body ) {
-	assert( name );
 	DeclarationNode * newnode = new DeclarationNode;
 	newnode->type = new TypeData( TypeData::Aggregate );
 	newnode->type->aggregate.kind = kind;
-	newnode->type->aggregate.name = name;
+	newnode->type->aggregate.name =  name == nullptr ? new string( DeclarationNode::anonymous.newName() ) : name;
 	newnode->type->aggregate.actuals = actuals;
 	newnode->type->aggregate.fields = fields;
@@ -283,14 +282,15 @@
 	newnode->type->aggregate.tagged = false;
 	newnode->type->aggregate.parent = nullptr;
+	newnode->type->aggregate.anon = name == nullptr;
 	return newnode;
 } // DeclarationNode::newAggregate
 
 DeclarationNode * DeclarationNode::newEnum( const string * name, DeclarationNode * constants, bool body ) {
-	assert( name );
 	DeclarationNode * newnode = new DeclarationNode;
 	newnode->type = new TypeData( TypeData::Enum );
-	newnode->type->enumeration.name = name;
+	newnode->type->enumeration.name = name == nullptr ? new string( DeclarationNode::anonymous.newName() ) : name;
 	newnode->type->enumeration.constants = constants;
 	newnode->type->enumeration.body = body;
+	newnode->type->enumeration.anon = name == nullptr;
 	return newnode;
 } // DeclarationNode::newEnum
@@ -971,10 +971,23 @@
 	for ( const DeclarationNode * cur = firstNode; cur; cur = dynamic_cast< DeclarationNode * >( cur->get_next() ) ) {
 		try {
+			bool extracted = false;
+			bool anon = false;
 			if ( DeclarationNode * extr = cur->extractAggregate() ) {
 				// handle the case where a structure declaration is contained within an object or type declaration
 				Declaration * decl = extr->build();
 				if ( decl ) {
+					// hoist the structure declaration
 					decl->location = cur->location;
 					* out++ = decl;
+
+					// need to remember the cases where a declaration contains an anonymous aggregate definition
+					extracted = true;
+					assert( extr->type );
+					if ( extr->type->kind == TypeData::Aggregate ) {
+						anon = extr->type->aggregate.anon;
+					} else if ( extr->type->kind == TypeData::Enum ) {
+						// xxx - is it useful to have an implicit anonymous enum member?
+						anon = extr->type->enumeration.anon;
+					}
 				} // if
 				delete extr;
@@ -983,6 +996,14 @@
 			Declaration * decl = cur->build();
 			if ( decl ) {
-				decl->location = cur->location;
-				* out++ = decl;
+				// don't include anonymous declaration for named aggregates, but do include them for anonymous aggregates, e.g.:
+				// struct S {
+				//   struct T { int x; }; // no anonymous member
+				//   struct { int y; };   // anonymous member
+				//   struct T;            // anonymous member
+				// };
+				if ( ! (extracted && decl->name == "" && ! anon) ) {
+					decl->location = cur->location;
+					* out++ = decl;
+				}
 			} // if
 		} catch( SemanticErrorException &e ) {
@@ -996,4 +1017,5 @@
 } // buildList
 
+// currently only builds assertions, function parameters, and return values
 void buildList( const DeclarationNode * firstNode, std::list< DeclarationWithType * > &outputList ) {
 	SemanticErrorException errors;
@@ -1008,5 +1030,5 @@
 				* out++ = dwt;
 			} else if ( StructDecl * agg = dynamic_cast< StructDecl * >( decl ) ) {
-				// xxx - this might be where anonymous struct members are added - should be conditional on struct name
+				// e.g., int foo(struct S) {}
 				StructInstType * inst = new StructInstType( Type::Qualifiers(), agg->name );
 				auto obj = new ObjectDecl( "", Type::StorageClasses(), linkage, nullptr, inst, nullptr );
@@ -1015,5 +1037,12 @@
 				delete agg;
 			} else if ( UnionDecl * agg = dynamic_cast< UnionDecl * >( decl ) ) {
+				// e.g., int foo(union U) {}
 				UnionInstType * inst = new UnionInstType( Type::Qualifiers(), agg->name );
+				auto obj = new ObjectDecl( "", Type::StorageClasses(), linkage, nullptr, inst, nullptr );
+				obj->location = cur->location;
+				* out++ = obj;
+			} else if ( EnumDecl * agg = dynamic_cast< EnumDecl * >( decl ) ) {
+				// e.g., int foo(enum E) {}
+				EnumInstType * inst = new EnumInstType( Type::Qualifiers(), agg->name );
 				auto obj = new ObjectDecl( "", Type::StorageClasses(), linkage, nullptr, inst, nullptr );
 				obj->location = cur->location;
Index: src/Parser/TypeData.cc
===================================================================
--- src/Parser/TypeData.cc	(revision aeec6b776d14e45c7d0eae9f5b5c1a1a6ab38bea)
+++ src/Parser/TypeData.cc	(revision 3d7e53b5573c6a90196213b47ca833bd80f2f16e)
@@ -63,4 +63,5 @@
 		enumeration.constants = nullptr;
 		enumeration.body = false;
+		enumeration.anon = false;
 		break;
 	  case Aggregate:
@@ -74,4 +75,5 @@
 		aggregate.tagged = false;
 		aggregate.parent = nullptr;
+		aggregate.anon = false;
 		break;
 	  case AggregateInst:
@@ -216,4 +218,5 @@
 		newtype->aggregate.fields = maybeClone( aggregate.fields );
 		newtype->aggregate.body = aggregate.body;
+		newtype->aggregate.anon = aggregate.anon;
 		newtype->aggregate.tagged = aggregate.tagged;
 		newtype->aggregate.parent = aggregate.parent ? new string( *aggregate.parent ) : nullptr;
@@ -228,4 +231,5 @@
 		newtype->enumeration.constants = maybeClone( enumeration.constants );
 		newtype->enumeration.body = enumeration.body;
+		newtype->enumeration.anon = enumeration.anon;
 		break;
 	  case Symbolic:
Index: src/Parser/TypeData.h
===================================================================
--- src/Parser/TypeData.h	(revision aeec6b776d14e45c7d0eae9f5b5c1a1a6ab38bea)
+++ src/Parser/TypeData.h	(revision 3d7e53b5573c6a90196213b47ca833bd80f2f16e)
@@ -36,4 +36,5 @@
 		DeclarationNode * fields;
 		bool body;
+		bool anon;
 
 		bool tagged;
@@ -57,4 +58,5 @@
 		DeclarationNode * constants;
 		bool body;
+		bool anon;
 	};
 
Index: src/Parser/parser.yy
===================================================================
--- src/Parser/parser.yy	(revision aeec6b776d14e45c7d0eae9f5b5c1a1a6ab38bea)
+++ src/Parser/parser.yy	(revision 3d7e53b5573c6a90196213b47ca833bd80f2f16e)
@@ -1857,5 +1857,5 @@
 aggregate_type:											// struct, union
 	aggregate_key attribute_list_opt '{' field_declaration_list_opt '}' type_parameters_opt
-		{ $$ = DeclarationNode::newAggregate( $1, new string( DeclarationNode::anonymous.newName() ), $6, $4, true )->addQualifiers( $2 ); }
+		{ $$ = DeclarationNode::newAggregate( $1, nullptr, $6, $4, true )->addQualifiers( $2 ); }
 	| aggregate_key attribute_list_opt no_attr_identifier fred
 		{
@@ -1981,5 +1981,5 @@
 enum_type:												// enum
 	ENUM attribute_list_opt '{' enumerator_list comma_opt '}'
-		{ $$ = DeclarationNode::newEnum( new string( DeclarationNode::anonymous.newName() ), $4, true )->addQualifiers( $2 ); }
+		{ $$ = DeclarationNode::newEnum( nullptr, $4, true )->addQualifiers( $2 ); }
 	| ENUM attribute_list_opt no_attr_identifier
 		{ typedefTable.makeTypedef( *$3 ); }
Index: src/tests/nested-types.c
===================================================================
--- src/tests/nested-types.c	(revision aeec6b776d14e45c7d0eae9f5b5c1a1a6ab38bea)
+++ src/tests/nested-types.c	(revision 3d7e53b5573c6a90196213b47ca833bd80f2f16e)
@@ -16,8 +16,8 @@
 typedef int N;
 struct A {
-  // forall(otype T) // xxx - should not be an error, but currently is
-  // struct N {
-  //   T x;
-  // };
+  forall(otype T) // xxx - should not be an error, but currently is
+  struct N {
+    T x;
+  };
 };
 
