Index: libcfa/src/containers/array.hfa
===================================================================
--- libcfa/src/containers/array.hfa	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ libcfa/src/containers/array.hfa	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -1,13 +1,7 @@
 
 
-// a type whose size is n
-#define Z(n) char[n]
-
-// the inverse of Z(-)
-#define z(N) sizeof(N)
-
-forall( T & ) struct tag {};
+forall( __CFA_tysys_id_only_X & ) struct tag {};
 #define ttag(T) ((tag(T)){})
-#define ztag(n) ttag(Z(n))
+#define ztag(n) ttag(n)
 
 
@@ -18,5 +12,5 @@
 forall( [N], S & | sized(S), Timmed &, Tbase & ) {
     struct arpk {
-        S strides[z(N)];
+        S strides[N];
     };
 
@@ -56,14 +50,14 @@
 
     static inline size_t ?`len( arpk(N, S, Timmed, Tbase) & a ) {
-        return z(N);
+        return N;
     }
 
     // workaround #226 (and array relevance thereof demonstrated in mike102/otype-slow-ndims.cfa)
     static inline void ?{}( arpk(N, S, Timmed, Tbase) & this ) {
-        void ?{}( S (&inner)[z(N)] ) {}
+        void ?{}( S (&inner)[N] ) {}
         ?{}(this.strides);
     }
     static inline void ^?{}( arpk(N, S, Timmed, Tbase) & this ) {
-        void ^?{}( S (&inner)[z(N)] ) {}
+        void ^?{}( S (&inner)[N] ) {}
         ^?{}(this.strides);
     }
Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/AST/Convert.cpp	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -2415,4 +2415,15 @@
 	}
 
+	virtual void visit( const DimensionExpr * old ) override final {
+		// DimensionExpr gets desugared away in Validate.
+		// As long as new-AST passes don't use it, this cheap-cheerful error
+		// detection helps ensure that these occurrences have been compiled
+		// away, as expected.  To move the DimensionExpr boundary downstream
+		// or move the new-AST translation boundary upstream, implement
+		// DimensionExpr in the new AST and implement a conversion.
+		(void) old;
+		assert(false && "DimensionExpr should not be present at new-AST boundary");
+	}
+
 	virtual void visit( const AsmExpr * old ) override final {
 		this->node = visitBaseExpr( old,
Index: src/AST/Decl.cpp
===================================================================
--- src/AST/Decl.cpp	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/AST/Decl.cpp	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -78,5 +78,5 @@
 
 const char * TypeDecl::typeString() const {
-	static const char * kindNames[] = { "sized data type", "sized data type", "sized object type", "sized function type", "sized tuple type", "sized array length type" };
+	static const char * kindNames[] = { "sized data type", "sized data type", "sized object type", "sized function type", "sized tuple type", "sized length value" };
 	static_assert( sizeof(kindNames) / sizeof(kindNames[0]) == TypeDecl::NUMBER_OF_KINDS, "typeString: kindNames is out of sync." );
 	assertf( kind < TypeDecl::NUMBER_OF_KINDS, "TypeDecl kind is out of bounds." );
Index: src/AST/Decl.hpp
===================================================================
--- src/AST/Decl.hpp	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/AST/Decl.hpp	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -175,5 +175,5 @@
 class TypeDecl final : public NamedTypeDecl {
   public:
-	enum Kind { Dtype, DStype, Otype, Ftype, Ttype, ALtype, NUMBER_OF_KINDS };
+	enum Kind { Dtype, DStype, Otype, Ftype, Ttype, Dimension, NUMBER_OF_KINDS };
 
 	Kind kind;
Index: src/CodeGen/CodeGenerator.cc
===================================================================
--- src/CodeGen/CodeGenerator.cc	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/CodeGen/CodeGenerator.cc	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -589,4 +589,9 @@
 			output << nameExpr->get_name();
 		} // if
+	}
+
+	void CodeGenerator::postvisit( DimensionExpr * dimensionExpr ) {
+		extension( dimensionExpr );
+		output << "/*non-type*/" << dimensionExpr->get_name();
 	}
 
Index: src/CodeGen/CodeGenerator.h
===================================================================
--- src/CodeGen/CodeGenerator.h	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/CodeGen/CodeGenerator.h	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -92,4 +92,5 @@
 		void postvisit( TupleIndexExpr * tupleExpr );
 		void postvisit( TypeExpr *typeExpr );
+		void postvisit( DimensionExpr *dimensionExpr );
 		void postvisit( AsmExpr * );
 		void postvisit( StmtExpr * );
Index: src/Common/PassVisitor.h
===================================================================
--- src/Common/PassVisitor.h	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/Common/PassVisitor.h	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -167,4 +167,6 @@
 	virtual void visit( TypeExpr * typeExpr ) override final;
 	virtual void visit( const TypeExpr * typeExpr ) override final;
+	virtual void visit( DimensionExpr * dimensionExpr ) override final;
+	virtual void visit( const DimensionExpr * dimensionExpr ) override final;
 	virtual void visit( AsmExpr * asmExpr ) override final;
 	virtual void visit( const AsmExpr * asmExpr ) override final;
@@ -309,4 +311,5 @@
 	virtual Expression * mutate( CommaExpr * commaExpr ) override final;
 	virtual Expression * mutate( TypeExpr * typeExpr ) override final;
+	virtual Expression * mutate( DimensionExpr * dimensionExpr ) override final;
 	virtual Expression * mutate( AsmExpr * asmExpr ) override final;
 	virtual Expression * mutate( ImplicitCopyCtorExpr * impCpCtorExpr ) override final;
@@ -542,5 +545,5 @@
 class WithIndexer {
 protected:
-	WithIndexer() {}
+	WithIndexer( bool trackIdentifiers = true ) : indexer(trackIdentifiers) {}
 	~WithIndexer() {}
 
Index: src/Common/PassVisitor.impl.h
===================================================================
--- src/Common/PassVisitor.impl.h	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/Common/PassVisitor.impl.h	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -2519,4 +2519,34 @@
 
 //--------------------------------------------------------------------------
+// DimensionExpr
+template< typename pass_type >
+void PassVisitor< pass_type >::visit( DimensionExpr * node ) {
+	VISIT_START( node );
+
+	indexerScopedAccept( node->result, *this );
+
+	VISIT_END( node );
+}
+
+template< typename pass_type >
+void PassVisitor< pass_type >::visit( const DimensionExpr * node ) {
+	VISIT_START( node );
+
+	indexerScopedAccept( node->result, *this );
+
+	VISIT_END( node );
+}
+
+template< typename pass_type >
+Expression * PassVisitor< pass_type >::mutate( DimensionExpr * node ) {
+	MUTATE_START( node );
+
+	indexerScopedMutate( node->env   , *this );
+	indexerScopedMutate( node->result, *this );
+
+	MUTATE_END( Expression, node );
+}
+
+//--------------------------------------------------------------------------
 // AsmExpr
 template< typename pass_type >
@@ -3157,5 +3187,5 @@
 
 	maybeAccept_impl( node->forall, *this );
-	// xxx - should PointerType visit/mutate dimension?
+	maybeAccept_impl( node->dimension, *this );
 	maybeAccept_impl( node->base, *this );
 
@@ -3168,5 +3198,5 @@
 
 	maybeAccept_impl( node->forall, *this );
-	// xxx - should PointerType visit/mutate dimension?
+	maybeAccept_impl( node->dimension, *this );
 	maybeAccept_impl( node->base, *this );
 
@@ -3179,5 +3209,5 @@
 
 	maybeMutate_impl( node->forall, *this );
-	// xxx - should PointerType visit/mutate dimension?
+	maybeMutate_impl( node->dimension, *this );
 	maybeMutate_impl( node->base, *this );
 
Index: src/Parser/DeclarationNode.cc
===================================================================
--- src/Parser/DeclarationNode.cc	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/Parser/DeclarationNode.cc	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -1076,8 +1076,8 @@
 	if ( variable.tyClass != TypeDecl::NUMBER_OF_KINDS ) {
 		// otype is internally converted to dtype + otype parameters
-		static const TypeDecl::Kind kindMap[] = { TypeDecl::Dtype, TypeDecl::DStype, TypeDecl::Dtype, TypeDecl::Ftype, TypeDecl::Ttype, TypeDecl::Dtype };
+		static const TypeDecl::Kind kindMap[] = { TypeDecl::Dtype, TypeDecl::DStype, TypeDecl::Dtype, TypeDecl::Ftype, TypeDecl::Ttype, TypeDecl::Dimension };
 		static_assert( sizeof(kindMap) / sizeof(kindMap[0]) == TypeDecl::NUMBER_OF_KINDS, "DeclarationNode::build: kindMap is out of sync." );
 		assertf( variable.tyClass < sizeof(kindMap)/sizeof(kindMap[0]), "Variable's tyClass is out of bounds." );
-		TypeDecl * ret = new TypeDecl( *name, Type::StorageClasses(), nullptr, kindMap[ variable.tyClass ], variable.tyClass == TypeDecl::Otype || variable.tyClass == TypeDecl::ALtype, variable.initializer ? variable.initializer->buildType() : nullptr );
+		TypeDecl * ret = new TypeDecl( *name, Type::StorageClasses(), nullptr, kindMap[ variable.tyClass ], variable.tyClass == TypeDecl::Otype, variable.initializer ? variable.initializer->buildType() : nullptr );
 		buildList( variable.assertions, ret->get_assertions() );
 		return ret;
Index: src/Parser/ExpressionNode.cc
===================================================================
--- src/Parser/ExpressionNode.cc	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/Parser/ExpressionNode.cc	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -509,4 +509,9 @@
 } // build_varref
 
+DimensionExpr * build_dimensionref( const string * name ) {
+	DimensionExpr * expr = new DimensionExpr( *name );
+	delete name;
+	return expr;
+} // build_varref
 // TODO: get rid of this and OperKinds and reuse code from OperatorTable
 static const char * OperName[] = {						// must harmonize with OperKinds
Index: src/Parser/ParseNode.h
===================================================================
--- src/Parser/ParseNode.h	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/Parser/ParseNode.h	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -183,4 +183,5 @@
 
 NameExpr * build_varref( const std::string * name );
+DimensionExpr * build_dimensionref( const std::string * name );
 
 Expression * build_cast( DeclarationNode * decl_node, ExpressionNode * expr_node );
Index: src/Parser/TypedefTable.cc
===================================================================
--- src/Parser/TypedefTable.cc	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/Parser/TypedefTable.cc	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -10,6 +10,6 @@
 // Created On       : Sat May 16 15:20:13 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Mon Mar 15 20:56:47 2021
-// Update Count     : 260
+// Last Modified On : Wed May 19 08:30:14 2021
+// Update Count     : 262
 //
 
@@ -31,4 +31,5 @@
 	switch ( kind ) {
 	  case IDENTIFIER: return "identifier";
+	  case TYPEDIMname: return "typedim";
 	  case TYPEDEFname: return "typedef";
 	  case TYPEGENname: return "typegen";
Index: src/Parser/parser.yy
===================================================================
--- src/Parser/parser.yy	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/Parser/parser.yy	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -10,6 +10,6 @@
 // Created On       : Sat Sep  1 20:22:55 2001
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Mon Apr 26 18:41:54 2021
-// Update Count     : 4990
+// Last Modified On : Wed May 19 14:20:36 2021
+// Update Count     : 5022
 //
 
@@ -287,5 +287,5 @@
 
 // names and constants: lexer differentiates between identifier and typedef names
-%token<tok> IDENTIFIER		QUOTED_IDENTIFIER	TYPEDEFname		TYPEGENname
+%token<tok> IDENTIFIER		QUOTED_IDENTIFIER	TYPEDIMname		TYPEDEFname		TYPEGENname
 %token<tok> TIMEOUT			WOR					CATCH			RECOVER			CATCHRESUME		FIXUP		FINALLY		// CFA
 %token<tok> INTEGERconstant	CHARACTERconstant	STRINGliteral
@@ -586,4 +586,8 @@
 	| quasi_keyword
 		{ $$ = new ExpressionNode( build_varref( $1 ) ); }
+	| TYPEDIMname										// CFA, generic length argument
+		// { $$ = new ExpressionNode( new TypeExpr( maybeMoveBuildType( DeclarationNode::newFromTypedef( $1 ) ) ) ); }
+		// { $$ = new ExpressionNode( build_varref( $1 ) ); }
+		{ $$ = new ExpressionNode( build_dimensionref( $1 ) ); }
 	| tuple
 	| '(' comma_expression ')'
@@ -2535,6 +2539,6 @@
 	| '[' identifier_or_type_name ']'
 		{
-			typedefTable.addToScope( *$2, TYPEDEFname, "9" );
-			$$ = DeclarationNode::newTypeParam( TypeDecl::ALtype, $2 );
+			typedefTable.addToScope( *$2, TYPEDIMname, "9" );
+			$$ = DeclarationNode::newTypeParam( TypeDecl::Dimension, $2 );
 		}
 	// | type_specifier identifier_parameter_declarator
@@ -2590,10 +2594,8 @@
 		{ $$ = new ExpressionNode( new TypeExpr( maybeMoveBuildType( $1 ) ) ); }
 	| assignment_expression
-		{ SemanticError( yylloc, toString("Expression generic parameters are currently unimplemented: ", $1->build()) ); $$ = nullptr; }
 	| type_list ',' type
 		{ $$ = (ExpressionNode *)($1->set_last( new ExpressionNode( new TypeExpr( maybeMoveBuildType( $3 ) ) ) )); }
 	| type_list ',' assignment_expression
-		{ SemanticError( yylloc, toString("Expression generic parameters are currently unimplemented: ", $3->build()) ); $$ = nullptr; }
-		// { $$ = (ExpressionNode *)( $1->set_last( $3 )); }
+		{ $$ = (ExpressionNode *)( $1->set_last( $3 )); }
 	;
 
Index: src/SymTab/Indexer.cc
===================================================================
--- src/SymTab/Indexer.cc	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/SymTab/Indexer.cc	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -74,7 +74,7 @@
 	}
 
-	Indexer::Indexer()
+	Indexer::Indexer( bool trackIdentifiers )
 	: idTable(), typeTable(), structTable(), enumTable(), unionTable(), traitTable(),
-	  prevScope(), scope( 0 ), repScope( 0 ) { ++* stats().count; }
+	  prevScope(), scope( 0 ), repScope( 0 ), trackIdentifiers( trackIdentifiers ) { ++* stats().count; }
 
 	Indexer::~Indexer() {
@@ -110,4 +110,6 @@
 
 	void Indexer::lookupId( const std::string & id, std::list< IdData > &out ) const {
+		assert( trackIdentifiers );
+
 		++* stats().lookup_calls;
 		if ( ! idTable ) return;
@@ -434,4 +436,5 @@
 			const Declaration * deleteStmt ) {
 		++* stats().add_calls;
+		if ( ! trackIdentifiers ) return;
 		const std::string &name = decl->name;
 		if ( name == "" ) return;
Index: src/SymTab/Indexer.h
===================================================================
--- src/SymTab/Indexer.h	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/SymTab/Indexer.h	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -31,5 +31,5 @@
 	class Indexer : public std::enable_shared_from_this<SymTab::Indexer> {
 	public:
-		explicit Indexer();
+		explicit Indexer( bool trackIdentifiers = true );
 		virtual ~Indexer();
 
@@ -180,4 +180,6 @@
 		/// returns true if there exists a declaration with C linkage and the given name with a different mangled name
 		bool hasIncompatibleCDecl( const std::string & id, const std::string & mangleName ) const;
+
+	    bool trackIdentifiers;
 	};
 } // namespace SymTab
Index: src/SymTab/Validate.cc
===================================================================
--- src/SymTab/Validate.cc	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/SymTab/Validate.cc	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -105,4 +105,5 @@
 
 	struct FixQualifiedTypes final : public WithIndexer {
+		FixQualifiedTypes() : WithIndexer(false) {}
 		Type * postmutate( QualifiedType * );
 	};
@@ -174,4 +175,14 @@
 	};
 
+	/// Does early resolution on the expressions that give enumeration constants their values
+	struct ResolveEnumInitializers final : public WithIndexer, public WithGuards, public WithVisitorRef<ResolveEnumInitializers>, public WithShortCircuiting {
+		ResolveEnumInitializers( const Indexer * indexer );
+		void postvisit( EnumDecl * enumDecl );
+
+	  private:
+		const Indexer * local_indexer;
+
+	};
+
 	/// Replaces array and function types in forall lists by appropriate pointer type and assigns each Object and Function declaration a unique ID.
 	struct ForallPointerDecay_old final {
@@ -260,4 +271,23 @@
 		void previsit( StructInstType * inst );
 		void previsit( UnionInstType * inst );
+	};
+
+	/// desugar declarations and uses of dimension paramaters like [N],
+	/// from type-system managed values, to tunnneling via ordinary types,
+	/// as char[-] in and sizeof(-) out
+	struct TranslateDimensionGenericParameters : public WithIndexer, public WithGuards {
+		static void translateDimensions( std::list< Declaration * > &translationUnit );
+		TranslateDimensionGenericParameters();
+
+		bool nextVisitedNodeIsChildOfSUIT = false; // SUIT = Struct or Union -Inst Type
+		bool visitingChildOfSUIT = false;
+		void changeState_ChildOfSUIT( bool newVal );
+		void premutate( StructInstType * sit );
+		void premutate( UnionInstType * uit );
+		void premutate( BaseSyntaxNode * node );
+
+		TypeDecl * postmutate( TypeDecl * td );
+		Expression * postmutate( DimensionExpr * de );
+		Expression * postmutate( Expression * e );
 	};
 
@@ -307,4 +337,5 @@
 		PassVisitor<EnumAndPointerDecay_old> epc;
 		PassVisitor<LinkReferenceToTypes_old> lrt( nullptr );
+		PassVisitor<ResolveEnumInitializers> rei( nullptr );
 		PassVisitor<ForallPointerDecay_old> fpd;
 		PassVisitor<CompoundLiteral> compoundliteral;
@@ -326,23 +357,27 @@
 			Stats::Heap::newPass("validate-B");
 			Stats::Time::BlockGuard guard("validate-B");
-			Stats::Time::TimeBlock("Link Reference To Types", [&]() {
-				acceptAll( translationUnit, lrt ); // must happen before autogen, because sized flag needs to propagate to generated functions
-			});
-			Stats::Time::TimeBlock("Fix Qualified Types", [&]() {
-				mutateAll( translationUnit, fixQual ); // must happen after LinkReferenceToTypes_old, because aggregate members are accessed
-			});
-			Stats::Time::TimeBlock("Hoist Structs", [&]() {
-				HoistStruct::hoistStruct( translationUnit ); // must happen after EliminateTypedef, so that aggregate typedefs occur in the correct order
-			});
-			Stats::Time::TimeBlock("Eliminate Typedefs", [&]() {
-				EliminateTypedef::eliminateTypedef( translationUnit ); //
-			});
+			acceptAll( translationUnit, lrt ); // must happen before autogen, because sized flag needs to propagate to generated functions
+			mutateAll( translationUnit, fixQual ); // must happen after LinkReferenceToTypes_old, because aggregate members are accessed
+			HoistStruct::hoistStruct( translationUnit );
+			EliminateTypedef::eliminateTypedef( translationUnit );
 		}
 		{
 			Stats::Heap::newPass("validate-C");
 			Stats::Time::BlockGuard guard("validate-C");
-			acceptAll( translationUnit, genericParams );  // check as early as possible - can't happen before LinkReferenceToTypes_old
-			ReturnChecker::checkFunctionReturns( translationUnit );
-			InitTweak::fixReturnStatements( translationUnit ); // must happen before autogen
+			Stats::Time::TimeBlock("Validate Generic Parameters", [&]() {
+				acceptAll( translationUnit, genericParams );  // check as early as possible - can't happen before LinkReferenceToTypes_old; observed failing when attempted before eliminateTypedef
+			});
+			Stats::Time::TimeBlock("Translate Dimensions", [&]() {
+				TranslateDimensionGenericParameters::translateDimensions( translationUnit );
+			});
+			Stats::Time::TimeBlock("Resolve Enum Initializers", [&]() {
+				acceptAll( translationUnit, rei ); // must happen after translateDimensions because rei needs identifier lookup, which needs name mangling
+			});
+			Stats::Time::TimeBlock("Check Function Returns", [&]() {
+				ReturnChecker::checkFunctionReturns( translationUnit );
+			});
+			Stats::Time::TimeBlock("Fix Return Statements", [&]() {
+				InitTweak::fixReturnStatements( translationUnit ); // must happen before autogen
+			});
 		}
 		{
@@ -644,5 +679,5 @@
 	}
 
-	LinkReferenceToTypes_old::LinkReferenceToTypes_old( const Indexer * other_indexer ) {
+	LinkReferenceToTypes_old::LinkReferenceToTypes_old( const Indexer * other_indexer ) : WithIndexer( false ) {
 		if ( other_indexer ) {
 			local_indexer = other_indexer;
@@ -664,12 +699,4 @@
 	}
 
-	void checkGenericParameters( ReferenceToType * inst ) {
-		for ( Expression * param : inst->parameters ) {
-			if ( ! dynamic_cast< TypeExpr * >( param ) ) {
-				SemanticError( inst, "Expression parameters for generic types are currently unsupported: " );
-			}
-		}
-	}
-
 	void LinkReferenceToTypes_old::postvisit( StructInstType * structInst ) {
 		const StructDecl * st = local_indexer->lookupStruct( structInst->name );
@@ -682,5 +709,4 @@
 			forwardStructs[ structInst->name ].push_back( structInst );
 		} // if
-		checkGenericParameters( structInst );
 	}
 
@@ -695,5 +721,4 @@
 			forwardUnions[ unionInst->name ].push_back( unionInst );
 		} // if
-		checkGenericParameters( unionInst );
 	}
 
@@ -807,13 +832,4 @@
 				forwardEnums.erase( fwds );
 			} // if
-
-			for ( Declaration * member : enumDecl->members ) {
-				ObjectDecl * field = strict_dynamic_cast<ObjectDecl *>( member );
-				if ( field->init ) {
-					// need to resolve enumerator initializers early so that other passes that determine if an expression is constexpr have the appropriate information.
-					SingleInit * init = strict_dynamic_cast<SingleInit *>( field->init );
-					ResolvExpr::findSingleExpression( init->value, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), indexer );
-				}
-			}
 		} // if
 	}
@@ -878,4 +894,25 @@
 				typeInst->set_isFtype( typeDecl->kind == TypeDecl::Ftype );
 			} // if
+		} // if
+	}
+
+	ResolveEnumInitializers::ResolveEnumInitializers( const Indexer * other_indexer ) : WithIndexer( true ) {
+		if ( other_indexer ) {
+			local_indexer = other_indexer;
+		} else {
+			local_indexer = &indexer;
+		} // if
+	}
+
+	void ResolveEnumInitializers::postvisit( EnumDecl * enumDecl ) {
+		if ( enumDecl->body ) {
+			for ( Declaration * member : enumDecl->members ) {
+				ObjectDecl * field = strict_dynamic_cast<ObjectDecl *>( member );
+				if ( field->init ) {
+					// need to resolve enumerator initializers early so that other passes that determine if an expression is constexpr have the appropriate information.
+					SingleInit * init = strict_dynamic_cast<SingleInit *>( field->init );
+					ResolvExpr::findSingleExpression( init->value, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), indexer );
+				}
+			}
 		} // if
 	}
@@ -1223,4 +1260,22 @@
 	}
 
+	// Test for special name on a generic parameter.  Special treatment for the
+	// special name is a bootstrapping hack.  In most cases, the worlds of T's
+	// and of N's don't overlap (normal treamtemt).  The foundations in
+	// array.hfa use tagging for both types and dimensions.  Tagging treats
+	// its subject parameter even more opaquely than T&, which assumes it is
+	// possible to have a pointer/reference to such an object.  Tagging only
+	// seeks to identify the type-system resident at compile time.  Both N's
+	// and T's can make tags.  The tag definition uses the special name, which
+	// is treated as "an N or a T."  This feature is not inteded to be used
+	// outside of the definition and immediate uses of a tag.
+	static inline bool isReservedTysysIdOnlyName( const std::string & name ) {
+		// name's prefix was __CFA_tysys_id_only, before it got wrapped in __..._generic
+		int foundAt = name.find("__CFA_tysys_id_only");
+		if (foundAt == 0) return true;
+		if (foundAt == 2 && name[0] == '_' && name[1] == '_') return true;
+		return false;
+	}
+
 	template< typename Aggr >
 	void validateGeneric( Aggr * inst ) {
@@ -1239,19 +1294,35 @@
 			TypeSubstitution sub;
 			auto paramIter = params->begin();
-			for ( size_t i = 0; paramIter != params->end(); ++paramIter, ++i ) {
-				if ( i < args.size() ) {
-					TypeExpr * expr = strict_dynamic_cast< TypeExpr * >( * std::next( args.begin(), i ) );
-					sub.add( (* paramIter)->get_name(), expr->get_type()->clone() );
-				} else if ( i == args.size() ) {
+			auto argIter = args.begin();
+			for ( ; paramIter != params->end(); ++paramIter, ++argIter ) {
+				if ( argIter != args.end() ) {
+					TypeExpr * expr = dynamic_cast< TypeExpr * >( * argIter );
+					if ( expr ) {
+						sub.add( (* paramIter)->get_name(), expr->get_type()->clone() );
+					}
+				} else {
 					Type * defaultType = (* paramIter)->get_init();
 					if ( defaultType ) {
 						args.push_back( new TypeExpr( defaultType->clone() ) );
 						sub.add( (* paramIter)->get_name(), defaultType->clone() );
+						argIter = std::prev(args.end());
+					} else {
+						SemanticError( inst, "Too few type arguments in generic type " );
 					}
 				}
+				assert( argIter != args.end() );
+				bool typeParamDeclared = (*paramIter)->kind != TypeDecl::Kind::Dimension;
+				bool typeArgGiven;
+				if ( isReservedTysysIdOnlyName( (*paramIter)->name ) ) {
+					// coerce a match when declaration is reserved name, which means "either"
+					typeArgGiven = typeParamDeclared;
+				} else {
+					typeArgGiven = dynamic_cast< TypeExpr * >( * argIter );
+				}
+				if ( ! typeParamDeclared &&   typeArgGiven ) SemanticError( inst, "Type argument given for value parameter: " );
+				if (   typeParamDeclared && ! typeArgGiven ) SemanticError( inst, "Expression argument given for type parameter: " );
 			}
 
 			sub.apply( inst );
-			if ( args.size() < params->size() ) SemanticError( inst, "Too few type arguments in generic type " );
 			if ( args.size() > params->size() ) SemanticError( inst, "Too many type arguments in generic type " );
 		}
@@ -1264,4 +1335,104 @@
 	void ValidateGenericParameters::previsit( UnionInstType * inst ) {
 		validateGeneric( inst );
+	}
+
+	void TranslateDimensionGenericParameters::translateDimensions( std::list< Declaration * > &translationUnit ) {
+		PassVisitor<TranslateDimensionGenericParameters> translator;
+		mutateAll( translationUnit, translator );
+	}
+
+	TranslateDimensionGenericParameters::TranslateDimensionGenericParameters() : WithIndexer( false ) {}
+
+	// Declaration of type variable:           forall( [N] )          ->  forall( N & | sized( N ) )
+	TypeDecl * TranslateDimensionGenericParameters::postmutate( TypeDecl * td ) {
+		if ( td->kind == TypeDecl::Dimension ) {
+			td->kind = TypeDecl::Dtype;
+			if ( ! isReservedTysysIdOnlyName( td->name ) ) {
+				td->sized = true;
+			}
+		}
+		return td;
+	}
+
+	// Situational awareness:
+	// array( float, [[currentExpr]]     )  has  visitingChildOfSUIT == true
+	// array( float, [[currentExpr]] - 1 )  has  visitingChildOfSUIT == false
+	// size_t x =    [[currentExpr]]        has  visitingChildOfSUIT == false
+	void TranslateDimensionGenericParameters::changeState_ChildOfSUIT( bool newVal ) {
+		GuardValue( nextVisitedNodeIsChildOfSUIT );
+		GuardValue( visitingChildOfSUIT );
+		visitingChildOfSUIT = nextVisitedNodeIsChildOfSUIT;
+		nextVisitedNodeIsChildOfSUIT = newVal;
+	}
+	void TranslateDimensionGenericParameters::premutate( StructInstType * sit ) {
+		(void) sit;
+		changeState_ChildOfSUIT(true);
+	}
+	void TranslateDimensionGenericParameters::premutate( UnionInstType * uit ) {
+		(void) uit;
+		changeState_ChildOfSUIT(true);
+	}
+	void TranslateDimensionGenericParameters::premutate( BaseSyntaxNode * node ) {
+		(void) node;
+		changeState_ChildOfSUIT(false);
+	}
+
+	// Passing values as dimension arguments:  array( float,     7 )  -> array( float, char[             7 ] )
+	// Consuming dimension parameters:         size_t x =    N - 1 ;  -> size_t x =          sizeof(N) - 1   ;
+	// Intertwined reality:                    array( float, N     )  -> array( float,              N        )
+	//                                         array( float, N - 1 )  -> array( float, char[ sizeof(N) - 1 ] )
+	// Intertwined case 1 is not just an optimization.
+	// Avoiding char[sizeof(-)] is necessary to enable the call of f to bind the value of N, in:
+	//   forall([N]) void f( array(float, N) & );
+	//   array(float, 7) a;
+	//   f(a);
+
+	Expression * TranslateDimensionGenericParameters::postmutate( DimensionExpr * de ) {
+		// Expression de is an occurrence of N in LHS of above examples.
+		// Look up the name that de references.
+		// If we are in a struct body, then this reference can be to an entry of the stuct's forall list.
+		// Whether or not we are in a struct body, this reference can be to an entry of a containing function's forall list.
+		// If we are in a struct body, then the stuct's forall declarations are innermost (functions don't occur in structs).
+		// Thus, a potential struct's declaration is highest priority.
+		// A struct's forall declarations are already renamed with _generic_ suffix.  Try that name variant first.
+
+		std::string useName = "__" + de->name + "_generic_";
+		TypeDecl * namedParamDecl = const_cast<TypeDecl *>( strict_dynamic_cast<const TypeDecl *, nullptr >( indexer.lookupType( useName ) ) );
+
+		if ( ! namedParamDecl ) {
+			useName = de->name;
+			namedParamDecl = const_cast<TypeDecl *>( strict_dynamic_cast<const TypeDecl *, nullptr >( indexer.lookupType( useName ) ) );
+		}
+
+		// Expect to find it always.  A misspelled name would have been parsed as an identifier.
+		assert( namedParamDecl && "Type-system-managed value name not found in symbol table" );
+
+		delete de;
+
+		TypeInstType * refToDecl = new TypeInstType( 0, useName, namedParamDecl );
+
+		if ( visitingChildOfSUIT ) {
+			// As in postmutate( Expression * ), topmost expression needs a TypeExpr wrapper
+			// But avoid ArrayType-Sizeof
+			return new TypeExpr( refToDecl );
+		} else {
+			// the N occurrence is being used directly as a runtime value,
+			// if we are in a type instantiation, then the N is within a bigger value computation
+			return new SizeofExpr( refToDecl );
+		}
+	}
+
+	Expression * TranslateDimensionGenericParameters::postmutate( Expression * e ) {
+		if ( visitingChildOfSUIT ) {
+			// e is an expression used as an argument to instantiate a type
+			if (! dynamic_cast< TypeExpr * >( e ) ) {
+				// e is a value expression
+				// but not a DimensionExpr, which has a distinct postmutate
+				Type * typeExprContent = new ArrayType( 0, new BasicType( 0, BasicType::Char ), e, true, false );
+				TypeExpr * result = new TypeExpr( typeExprContent );
+				return result;
+			}
+		}
+		return e;
 	}
 
Index: src/SynTree/Declaration.h
===================================================================
--- src/SynTree/Declaration.h	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/SynTree/Declaration.h	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -201,5 +201,5 @@
 	typedef NamedTypeDecl Parent;
   public:
-	enum Kind { Dtype, DStype, Otype, Ftype, Ttype, ALtype, NUMBER_OF_KINDS };
+	enum Kind { Dtype, DStype, Otype, Ftype, Ttype, Dimension, NUMBER_OF_KINDS };
 
 	Kind kind;
Index: src/SynTree/Expression.h
===================================================================
--- src/SynTree/Expression.h	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/SynTree/Expression.h	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -587,4 +587,23 @@
 };
 
+/// DimensionExpr represents a type-system provided value used in an expression ( forrall([N]) ... N + 1 )
+class DimensionExpr : public Expression {
+  public:
+	std::string name;
+
+	DimensionExpr( std::string name );
+	DimensionExpr( const DimensionExpr & other );
+	virtual ~DimensionExpr();
+
+	const std::string & get_name() const { return name; }
+	void set_name( std::string newValue ) { name = newValue; }
+
+	virtual DimensionExpr * clone() const override { return new DimensionExpr( * this ); }
+	virtual void accept( Visitor & v ) override { v.visit( this ); }
+	virtual void accept( Visitor & v ) const override { v.visit( this ); }
+	virtual Expression * acceptMutator( Mutator & m ) override { return m.mutate( this ); }
+	virtual void print( std::ostream & os, Indenter indent = {} ) const override;
+};
+
 /// AsmExpr represents a GCC 'asm constraint operand' used in an asm statement: [output] "=f" (result)
 class AsmExpr : public Expression {
Index: src/SynTree/Mutator.h
===================================================================
--- src/SynTree/Mutator.h	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/SynTree/Mutator.h	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -80,4 +80,5 @@
 	virtual Expression * mutate( CommaExpr * commaExpr ) = 0;
 	virtual Expression * mutate( TypeExpr * typeExpr ) = 0;
+	virtual Expression * mutate( DimensionExpr * dimensionExpr ) = 0;
 	virtual Expression * mutate( AsmExpr * asmExpr ) = 0;
 	virtual Expression * mutate( ImplicitCopyCtorExpr * impCpCtorExpr ) = 0;
Index: src/SynTree/SynTree.h
===================================================================
--- src/SynTree/SynTree.h	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/SynTree/SynTree.h	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -85,4 +85,5 @@
 class CommaExpr;
 class TypeExpr;
+class DimensionExpr;
 class AsmExpr;
 class ImplicitCopyCtorExpr;
Index: src/SynTree/TypeDecl.cc
===================================================================
--- src/SynTree/TypeDecl.cc	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/SynTree/TypeDecl.cc	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -33,5 +33,5 @@
 
 const char * TypeDecl::typeString() const {
-	static const char * kindNames[] = { "sized data type", "sized data type", "sized object type", "sized function type", "sized tuple type", "sized array length type" };
+	static const char * kindNames[] = { "sized data type", "sized data type", "sized object type", "sized function type", "sized tuple type", "sized length value" };
 	static_assert( sizeof(kindNames) / sizeof(kindNames[0]) == TypeDecl::NUMBER_OF_KINDS, "typeString: kindNames is out of sync." );
 	assertf( kind < TypeDecl::NUMBER_OF_KINDS, "TypeDecl kind is out of bounds." );
Index: src/SynTree/TypeExpr.cc
===================================================================
--- src/SynTree/TypeExpr.cc	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/SynTree/TypeExpr.cc	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -35,4 +35,18 @@
 }
 
+DimensionExpr::DimensionExpr( std::string name ) : Expression(), name(name) {
+	assertf(name != "0", "Zero is not a valid name");
+	assertf(name != "1", "One is not a valid name");
+}
+
+DimensionExpr::DimensionExpr( const DimensionExpr & other ) : Expression( other ), name( other.name ) {
+}
+
+DimensionExpr::~DimensionExpr() {}
+
+void DimensionExpr::print( std::ostream & os, Indenter indent ) const {
+	os << "Type-Sys Value: " << get_name();
+	Expression::print( os, indent );
+}
 // Local Variables: //
 // tab-width: 4 //
Index: src/SynTree/Visitor.h
===================================================================
--- src/SynTree/Visitor.h	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ src/SynTree/Visitor.h	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -135,4 +135,6 @@
 	virtual void visit( TypeExpr * node ) { visit( const_cast<const TypeExpr *>(node) ); }
 	virtual void visit( const TypeExpr * typeExpr ) = 0;
+	virtual void visit( DimensionExpr * node ) { visit( const_cast<const DimensionExpr *>(node) ); }
+	virtual void visit( const DimensionExpr * typeExpr ) = 0;
 	virtual void visit( AsmExpr * node ) { visit( const_cast<const AsmExpr *>(node) ); }
 	virtual void visit( const AsmExpr * asmExpr ) = 0;
Index: tests/array-container/.expect/language-dim-mismatch.txt
===================================================================
--- tests/array-container/.expect/language-dim-mismatch.txt	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
+++ tests/array-container/.expect/language-dim-mismatch.txt	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -0,0 +1,10 @@
+array-container/language-dim-mismatch.cfa:12:1 error: Type argument given for value parameter: instance of struct SN with body 1
+... with parameters
+  float
+
+array-container/language-dim-mismatch.cfa:13:1 error: Expression argument given for type parameter: instance of struct ST with body 1
+... with parameters
+  constant expression (42 42: signed int)
+  with resolved type:
+    signed int
+
Index: tests/array-container/array-basic.cfa
===================================================================
--- tests/array-container/array-basic.cfa	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ tests/array-container/array-basic.cfa	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -61,17 +61,17 @@
 forall( [Nw], [Nx], [Ny], [Nz] )
 void fillHelloData( array( float, Nw, Nx, Ny, Nz ) & wxyz ) {
-    for (w; z(Nw))
-    for (x; z(Nx))
-    for (y; z(Ny))
-    for (z; z(Nz))
+    for (w; Nw)
+    for (x; Nx)
+    for (y; Ny)
+    for (z; Nz)
         wxyz[w][x][y][z] = getMagicNumber(w, x, y, z);
 }
 
-forall( [Zn]
+forall( [N]
       , S & | sized(S)
       )
-float total1d_low( arpk(Zn, S, float, float ) & a ) {
+float total1d_low( arpk(N, S, float, float ) & a ) {
     float total = 0.0f;
-    for (i; z(Zn))
+    for (i; N)
         total += a[i];
     return total;
@@ -98,5 +98,5 @@
 
     expect = 0;
-    for (i; z(Nw))
+    for (i; Nw)
         expect += getMagicNumber( i, slice_ix, slice_ix, slice_ix );
     printf("expect Ws             = %f\n", expect);
@@ -117,5 +117,5 @@
 
     expect = 0;
-    for (i; z(Nx))
+    for (i; Nx)
         expect += getMagicNumber( slice_ix, i, slice_ix, slice_ix );
     printf("expect Xs             = %f\n", expect);
Index: tests/array-container/array-md-sbscr-cases.cfa
===================================================================
--- tests/array-container/array-md-sbscr-cases.cfa	(revision dcbfcbc5b3b9231d2f0ddc5dc07e9986e2843cd7)
+++ tests/array-container/array-md-sbscr-cases.cfa	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -20,8 +20,8 @@
 forall( [Nw], [Nx], [Ny], [Nz] )
 void fillHelloData( array( float, Nw, Nx, Ny, Nz ) & wxyz ) {
-    for (w; z(Nw))
-    for (x; z(Nx))
-    for (y; z(Ny))
-    for (z; z(Nz))
+    for (w; Nw)
+    for (x; Nx)
+    for (y; Ny)
+    for (z; Nz)
         wxyz[w][x][y][z] = getMagicNumber(w, x, y, z);
 }
@@ -246,25 +246,25 @@
     assert(( wxyz[[2,  3,  4,  5]] == valExpected ));
 
-    for ( i; z(Nw) ) {
+    for ( i; Nw ) {
         assert(( wxyz[[ i, 3, 4, 5 ]] == getMagicNumber(i, 3, 4, 5) ));
     }
 
-    for ( i; z(Nx) ) {
+    for ( i; Nx ) {
         assert(( wxyz[[ 2, i, 4, 5 ]] == getMagicNumber(2, i, 4, 5) ));
     }
 
-    for ( i; z(Ny) ) {
+    for ( i; Ny ) {
         assert(( wxyz[[ 2, 3, i, 5 ]] == getMagicNumber(2, 3, i, 5) ));
     }
 
-    for ( i; z(Nz) ) {
+    for ( i; Nz ) {
         assert(( wxyz[[ 2, 3, 4, i ]] == getMagicNumber(2, 3, 4, i) ));
     }
 
-    for ( i; z(Nw) ) {
+    for ( i; Nw ) {
         assert(( wxyz[[ i, all, 4, 5 ]][3] == getMagicNumber(i, 3, 4, 5) ));
     }
 
-    for ( i; z(Nw) ) {
+    for ( i; Nw ) {
         assert(( wxyz[[ all, 3, 4, 5 ]][i] == getMagicNumber(i, 3, 4, 5) ));
     }
Index: tests/array-container/language-dim-mismatch.cfa
===================================================================
--- tests/array-container/language-dim-mismatch.cfa	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
+++ tests/array-container/language-dim-mismatch.cfa	(revision 6e50a6bc841ea438631989230a869a5ede7467e8)
@@ -0,0 +1,15 @@
+forall( [N] )
+struct SN {};
+
+forall( T )
+struct ST {};
+
+int main() {
+
+    SN(42) good1;
+    ST(float) good2;
+
+    SN(float) bad1;  // first  expected error: Type argument given for value parameter
+    ST(42) bad2;     // second expected error: Expression argument given for type parameter
+
+}
