Index: src/AST/Decl.cpp
===================================================================
--- src/AST/Decl.cpp	(revision 2bb4a01b67c9bef5d552f56c14abeca79a2985d2)
+++ src/AST/Decl.cpp	(revision a300e4a85b7745f590bfbef5fc5182df7a469c97)
@@ -14,12 +14,15 @@
 //
 
+#include <cassert>        // for assert, strict_dynamic_cast
 #include <unordered_map>
 
 #include "Decl.hpp"
 
-#include "Fwd.hpp"   // for UniqueId
-#include "Node.hpp"  // for readonly
+#include "Fwd.hpp"        // for UniqueId
+#include "Init.hpp"
+#include "Node.hpp"       // for readonly
 
 namespace ast {
+
 // To canonicalize declarations
 static UniqueId lastUniqueId = 0;
@@ -39,4 +42,37 @@
 	return {};
 }
+
+// --- EnumDecl
+
+bool EnumDecl::valueOf( Decl* enumerator, long long& value ) const {
+	if ( enumValues.empty() ) {
+		long long crntVal = 0;
+		for ( const Decl* member : members ) {
+			const ObjectDecl* field = strict_dynamic_cast< const ObjectDecl* >( member );
+			if ( field->init ) {
+				const SingleInit* init = strict_dynamic_cast< const SingleInit* >( field->init );
+				auto result = eval( init->value );
+				if ( ! result.second ) {
+					SemanticError( init->location, toString( "Non-constexpr in initialization of "
+						"enumerator: ", field ) );
+				}
+				crntVal = result.first;
+			}
+			if ( enumValues.count( field->name ) != 0 ) {
+				SemanticError( location, toString( "Enum ", name, " has multiple members with the " 	"name ", field->name ) );
+			}
+			enumValues[ field->name ] = crntVal;
+			++crntVal;
+		}
+	}
+
+	auto it = enumValues.find( enumerator->name );
+	if ( it != enumValues.end() ) {
+		value = it->second;
+		return true;
+	}
+	return false;
+}
+
 }
 
Index: src/AST/Decl.hpp
===================================================================
--- src/AST/Decl.hpp	(revision 2bb4a01b67c9bef5d552f56c14abeca79a2985d2)
+++ src/AST/Decl.hpp	(revision a300e4a85b7745f590bfbef5fc5182df7a469c97)
@@ -16,7 +16,9 @@
 #pragma once
 
-#include <string>
+#include <string>              // for string, to_string
+#include <unordered_map>
 #include <vector>
 
+#include "FunctionSpec.hpp"
 #include "Fwd.hpp"             // for UniqueId
 #include "LinkageSpec.hpp"
@@ -24,9 +26,13 @@
 #include "ParseNode.hpp"
 #include "StorageClasses.hpp"
+#include "Type.hpp"            // for Type, ptr<Type>
 #include "Visitor.hpp"
+#include "Parser/ParseNode.h"  // for DeclarationNode::Aggregate
 
 namespace ast {
+
 class Attribute;
 class Expr;
+class TypeDecl;
 
 /// Base declaration class
@@ -66,5 +72,5 @@
 
 	std::vector<ptr<Attribute>> attributes;
-	Function::Specs funcSpecs;
+	Function::Specs funcSpec;
 	ptr<Expr> asmName;
 	bool isDeleted = false;
@@ -73,5 +79,106 @@
 		Linkage::Spec linkage, std::vector<ptr<Attribute>>&& attrs, Function::Specs fs )
 	: Decl( loc, name, storage, linkage ), mangleName(), attributes( std::move(attrs) ), 
-		funcSpecs(fs), asmName() {}
+		funcSpec(fs), asmName() {}
+	
+	std::string scopedMangleName() const { return mangleName + "_" + std::to_string(scopeLevel); }
+
+	/// Get type of this declaration. May be generated by subclass
+	virtual const Type* type() const = 0;
+	/// Set type of this declaration. May be verified by subclass
+	virtual void set_type(Type*) = 0;
+
+	virtual DeclWithType* accept( Visitor& v ) override = 0;
+private:
+	virtual DeclWithType* clone() const override = 0;
+};
+
+/// Aggregate type declaration base class
+class AggregateDecl : public Decl {
+public:
+	std::vector<ptr<Decl>> members;
+	std::vector<ptr<TypeDecl>> parameters;
+	std::vector<ptr<Attribute>> attributes;
+	bool body = false;
+	readonly<AggregateDecl> parent = {};
+
+	AggregateDecl( const CodeLocation& loc, const std::string& name, 
+		std::vector<ptr<Attribute>>&& attrs = {}, Linkage::Spec linkage = Linkage::Cforall )
+	: Decl( loc, name, Storage::Classes{}, linkage ), members(), parameters(), 
+	  attributes( std::move(attrs) ) {}
+	
+	AggregateDecl* set_body( bool b ) { body = b; return this; }
+
+protected:
+	/// Produces a name for the kind of aggregate
+	virtual std::string typeString() const = 0;
+};
+
+/// struct declaration `struct Foo { ... };`
+class StructDecl final : public AggregateDecl {
+public:
+	DeclarationNode::Aggregate kind;
+
+	StructDecl( const CodeLocation& loc, const std::string& name, 
+		DeclarationNode::Aggregate kind = DeclarationNode::Struct, 
+		std::vector<ptr<Attribute>>&& attrs = {}, Linkage::Spec linkage = Linkage::Cforall )
+	: AggregateDecl( loc, name, std::move(attrs), linkage ), kind( kind ) {}
+
+	bool is_coroutine() { return kind == DeclarationNode::Coroutine; }
+	bool is_monitor() { return kind == DeclarationNode::Monitor; }
+	bool is_thread() { return kind == DeclarationNode::Thread; }
+
+	Decl* accept( Visitor& v ) override { return v.visit( this ); }
+private:
+	StructDecl* clone() const override { return new StructDecl{ *this }; }
+
+	std::string typeString() const override { return "struct"; }
+};
+
+/// union declaration `union Foo { ... };`
+class UnionDecl final : public AggregateDecl {
+public:
+	UnionDecl( const CodeLocation& loc, const std::string& name, 
+		std::vector<ptr<Attribute>>&& attrs = {}, Linkage::Spec linkage = Linkage::Cforall )
+	: AggregateDecl( loc, name, std::move(attrs), linkage ) {}
+
+	Decl* accept( Visitor& v ) override { return v.visit( this ); }
+private:
+	UnionDecl* clone() const override { return new UnionDecl{ *this }; }
+
+	std::string typeString() const override { return "union"; }
+};
+
+/// enum declaration `enum Foo { ... };`
+class EnumDecl final : public AggregateDecl {
+public:
+	EnumDecl( const CodeLocation& loc, const std::string& name, 
+		std::vector<ptr<Attribute>>&& attrs = {}, Linkage::Spec linkage = Linkage::Cforall )
+	: AggregateDecl( loc, name, std::move(attrs), linkage ), enumValues() {}
+
+	/// gets the integer value for this enumerator, returning true iff value found
+	bool valueOf( Decl* enumerator, long long& value ) const;
+
+	Decl* accept( Visitor& v ) override { return v.visit( this ); }
+private:
+	EnumDecl* clone() const override { return new EnumDecl{ *this }; }
+
+	std::string typeString() const override { return "enum"; }
+
+	/// Map from names to enumerator values; kept private for lazy initialization
+	mutable std::unordered_map< std::string, long long > enumValues;
+};
+
+/// trait declaration `trait Foo( ... ) { ... };`
+class TraitDecl final : public AggregateDecl {
+public:
+	TraitDecl( const CodeLocation& loc, const std::string& name, 
+		std::vector<ptr<Attribute>>&& attrs = {}, Linkage::Spec linkage = Linkage::Cforall )
+	: AggregateDecl( loc, name, std::move(attrs), linkage ) {}
+
+	Decl* accept( Visitor& v ) override { return v.visit( this ); }
+private:
+	TraitDecl* clone() const override { return new TraitDecl{ *this }; }
+
+	std::string typeString() const override { return "trait"; }
 };
 
Index: src/AST/FunctionSpec.hpp
===================================================================
--- src/AST/FunctionSpec.hpp	(revision a300e4a85b7745f590bfbef5fc5182df7a469c97)
+++ src/AST/FunctionSpec.hpp	(revision a300e4a85b7745f590bfbef5fc5182df7a469c97)
@@ -0,0 +1,52 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// FunctionSpec.hpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Thu May 9 10:00:00 2019
+// Last Modified By : Aaron B. Moss
+// Last Modified On : Thu May 9 10:00:00 2019
+// Update Count     : 1
+//
+
+#pragma once
+
+#include "Bitfield.hpp"
+
+namespace ast {
+
+namespace Function {
+
+	/// Bitflags for function specifiers
+	enum {
+		Inline   = 1 << 0,
+		Noreturn = 1 << 1,
+		Fortran  = 1 << 2,
+		NumSpecs      = 3
+	};
+
+	/// Bitflag type for storage classes
+	union Specs {
+		unsigned int val;
+		struct {
+			bool is_inline   : 1;
+			bool is_noreturn : 1;
+			bool is_fortran  : 1;
+		};
+
+		MakeBitfield( Specs )
+		MakeBitfieldPrint( NumSpecs )
+	};
+
+}
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/AST/LinkageSpec.cpp
===================================================================
--- src/AST/LinkageSpec.cpp	(revision 2bb4a01b67c9bef5d552f56c14abeca79a2985d2)
+++ src/AST/LinkageSpec.cpp	(revision a300e4a85b7745f590bfbef5fc5182df7a469c97)
@@ -24,34 +24,37 @@
 
 namespace ast {
-	namespace Linkage {
-		Spec update( CodeLocation loc, Spec spec, const std::string * cmd ) {
-			assert( cmd );
-			std::unique_ptr<const std::string> guard( cmd ); // allocated by lexer
-			if ( *cmd == "\"Cforall\"" ) {
-				spec.is_mangled = true;
-				return spec;
-			} else if ( *cmd == "\"C\"" ) {
-				spec.is_mangled = false;
-				return spec;
-			} else {
-				SemanticError( loc, "Invalid linkage specifier " + *cmd );
-			}
+
+namespace Linkage {
+
+	Spec update( CodeLocation loc, Spec spec, const std::string * cmd ) {
+		assert( cmd );
+		std::unique_ptr<const std::string> guard( cmd ); // allocated by lexer
+		if ( *cmd == "\"Cforall\"" ) {
+			spec.is_mangled = true;
+			return spec;
+		} else if ( *cmd == "\"C\"" ) {
+			spec.is_mangled = false;
+			return spec;
+		} else {
+			SemanticError( loc, "Invalid linkage specifier " + *cmd );
 		}
+	}
+
+
+	std::string name( Spec spec ) {
+		switch ( spec ) {
+		case Intrinsic:  return "intrinsic";
+		case C:          return "C";
+		case Cforall:    return "Cforall";
+		case AutoGen:    return "autogenerated cfa";
+		case Compiler:   return "compiler built-in";
+		case BuiltinCFA: return "cfa built-in";
+		case BuiltinC:   return "c built-in";
+		default:         return "<unnamed linkage spec>";
+		}
+	}
 	
+}
 
-		std::string name( Spec spec ) {
-			switch ( spec ) {
-			case Intrinsic:  return "intrinsic";
-			case C:          return "C";
-			case Cforall:    return "Cforall";
-			case AutoGen:    return "autogenerated cfa";
-			case Compiler:   return "compiler built-in";
-			case BuiltinCFA: return "cfa built-in";
-			case BuiltinC:   return "c built-in";
-			default:         return "<unnamed linkage spec>";
-			}
-		}
-
-	}
 }
 
Index: src/AST/LinkageSpec.hpp
===================================================================
--- src/AST/LinkageSpec.hpp	(revision 2bb4a01b67c9bef5d552f56c14abeca79a2985d2)
+++ src/AST/LinkageSpec.hpp	(revision a300e4a85b7745f590bfbef5fc5182df7a469c97)
@@ -23,53 +23,54 @@
 namespace ast {
 
-	namespace Linkage {
+namespace Linkage {
 
-		/// Bitflags for linkage specifiers
-		enum {
-			Mangle       = 1 << 0,
-			Generate     = 1 << 1,
-			Overrideable = 1 << 2,
-			Builtin      = 1 << 3,
-			GccBuiltin   = 1 << 4
+	/// Bitflags for linkage specifiers
+	enum {
+		Mangle       = 1 << 0,
+		Generate     = 1 << 1,
+		Overrideable = 1 << 2,
+		Builtin      = 1 << 3,
+		GccBuiltin   = 1 << 4
+	};
+
+	/// Bitflag type for storage classes
+	union Spec {
+		unsigned int val;
+		struct {
+			bool is_mangled      : 1;
+			bool is_generatable  : 1;
+			bool is_overrideable : 1;
+			bool is_builtin      : 1;
+			bool is_gcc_builtin  : 1;
 		};
 
-		/// Bitflag type for storage classes
-		union Spec {
-			unsigned int val;
-			struct {
-				bool is_mangled      : 1;
-				bool is_generatable  : 1;
-				bool is_overrideable : 1;
-				bool is_builtin      : 1;
-				bool is_gcc_builtin  : 1;
-			};
+		MakeBitfield( Spec )
+	};
 
-			MakeBitfield( Spec )
-		};
+	/// If `cmd` = "C" returns `spec` with `is_mangled = false`.
+	/// If `cmd` = "Cforall" returns `spec` with `is_mangled = true`. 
+	Spec update( CodeLocation loc, Spec spec, const std::string * cmd );
 
-		/// If `cmd` = "C" returns `spec` with `is_mangled = false`.
-		/// If `cmd` = "Cforall" returns `spec` with `is_mangled = true`. 
-		Spec update( CodeLocation loc, Spec spec, const std::string * cmd );
+	/// A human-readable name for this spec
+	std::string name( Spec spec );
 
-		/// A human-readable name for this spec
-		std::string name( Spec spec );
+	// Pre-defined flag combinations
+	
+	/// C built-in defined in prelude
+	constexpr Spec Intrinsic  = { Mangle | Generate | Overrideable | Builtin };
+	/// Ordinary Cforall
+	constexpr Spec Cforall    = { Mangle | Generate };
+	/// C code: not overloadable, not mangled
+	constexpr Spec C          = { Generate };
+	/// Built by translator (e.g. struct assignment)
+	constexpr Spec AutoGen    = { Mangle | Generate | Overrideable };
+	/// GCC internal
+	constexpr Spec Compiler   = { Mangle | Builtin | GccBuiltin };
+	/// Mangled builtins
+	constexpr Spec BuiltinCFA = { Mangle | Generate | Builtin };
+	/// Non-mangled builtins
+	constexpr Spec BuiltinC   = { Generate | Builtin };
+}
 
-		// Pre-defined flag combinations
-		
-		/// C built-in defined in prelude
-		constexpr Spec Intrinsic  = { Mangle | Generate | Overrideable | Builtin };
-		/// Ordinary Cforall
-		constexpr Spec Cforall    = { Mangle | Generate };
-		/// C code: not overloadable, not mangled
-		constexpr Spec C          = { Generate };
-		/// Built by translator (e.g. struct assignment)
-		constexpr Spec AutoGen    = { Mangle | Generate | Overrideable };
-		/// GCC internal
-		constexpr Spec Compiler   = { Mangle | Builtin | GccBuiltin };
-		/// Mangled builtins
-		constexpr Spec BuiltinCFA = { Mangle | Generate | Builtin };
-		/// Non-mangled builtins
-		constexpr Spec BuiltinC   = { Generate | Builtin };
-	}
 }
 
Index: src/AST/StorageClasses.hpp
===================================================================
--- src/AST/StorageClasses.hpp	(revision 2bb4a01b67c9bef5d552f56c14abeca79a2985d2)
+++ src/AST/StorageClasses.hpp	(revision a300e4a85b7745f590bfbef5fc5182df7a469c97)
@@ -20,32 +20,32 @@
 namespace ast {
 
-	namespace Storage {
+namespace Storage {
 
-		/// Bitflags for storage classes
-		enum {
-			Extern      = 1 << 0,
-			Static      = 1 << 1,
-			Auto        = 1 << 2,
-			Register    = 1 << 3,
-			ThreadLocal = 1 << 4,
-			NumClasses       = 5
+	/// Bitflags for storage classes
+	enum {
+		Extern      = 1 << 0,
+		Static      = 1 << 1,
+		Auto        = 1 << 2,
+		Register    = 1 << 3,
+		ThreadLocal = 1 << 4,
+		NumClasses       = 5
+	};
+
+	/// Bitflag type for storage classes
+	union Classes {
+		unsigned int val;
+		struct {
+			bool is_extern      : 1;
+			bool is_static      : 1;
+			bool is_auto        : 1;
+			bool is_register    : 1;
+			bool is_threadlocal : 1;
 		};
 
-		/// Bitflag type for storage classes
-		union Classes {
-			unsigned int val;
-			struct {
-				bool is_extern      : 1;
-				bool is_static      : 1;
-				bool is_auto        : 1;
-				bool is_register    : 1;
-				bool is_threadlocal : 1;
-			};
+		MakeBitfield( Classes )
+		MakeBitfieldPrint( NumClasses )
+	};
 
-			MakeBitfield( Classes )
-			MakeBitfieldPrint( NumClasses )
-		};
-
-	}
+}
 }
 
Index: src/AST/porting.md
===================================================================
--- src/AST/porting.md	(revision 2bb4a01b67c9bef5d552f56c14abeca79a2985d2)
+++ src/AST/porting.md	(revision a300e4a85b7745f590bfbef5fc5182df7a469c97)
@@ -55,5 +55,5 @@
 * Preserve names from previous AST whenever reasonable, and get team consensus on any changes.
 * Strong justification required for private fields
-  * No `get_` prefix on getters
+  * No `get_` prefix on getters (including for generated fields)
 * Notable changes:
   * for concision and consistency with subclasses:
@@ -81,4 +81,10 @@
 `DeclWithType`
 * When `SymTab::Validate::Pass2` is rewritten, update comment on `mangleName` with new name of pass
+* `get_scopedMangleName()` => `scopedMangleName()`
+* `get_type()` => `type()`
+  * now returns `const Type*` so can't be inadvertently mutated
+
+`EnumDecl`
+* **TODO** rebuild `eval` for new AST (re: `valueOf` implementation)
 
 `Expr`
