Index: src/CodeGen/CodeGenerator.cc
===================================================================
--- src/CodeGen/CodeGenerator.cc	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/CodeGen/CodeGenerator.cc	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -1012,4 +1012,12 @@
 	}
 
+	void CodeGenerator::postvisit( WithStmt * with ) {
+		if ( ! genC ) {
+			output << "with ( ";
+			genCommaList( with->exprs.begin(), with->exprs.end() );
+			output << " ) ";
+		}
+		with->stmt->accept( *visitor );
+	}
 
 	void CodeGenerator::postvisit( WhileStmt * whileStmt ) {
Index: src/CodeGen/CodeGenerator.h
===================================================================
--- src/CodeGen/CodeGenerator.h	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/CodeGen/CodeGenerator.h	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -102,4 +102,5 @@
 		void postvisit( CatchStmt * );
 		void postvisit( WaitForStmt * );
+		void postvisit( WithStmt * );
 		void postvisit( WhileStmt * );
 		void postvisit( ForStmt * );
Index: src/Common/PassVisitor.h
===================================================================
--- src/Common/PassVisitor.h	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/Common/PassVisitor.h	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -81,4 +81,5 @@
 	virtual void visit( FinallyStmt * finallyStmt ) override final;
 	virtual void visit( WaitForStmt * waitforStmt ) override final;
+	virtual void visit( WithStmt * withStmt ) override final;
 	virtual void visit( NullStmt * nullStmt ) override final;
 	virtual void visit( DeclStmt * declStmt ) override final;
@@ -172,4 +173,5 @@
 	virtual Statement * mutate( FinallyStmt * finallyStmt ) override final;
 	virtual Statement * mutate( WaitForStmt * waitforStmt ) override final;
+	virtual Statement * mutate( WithStmt * withStmt ) override final;
 	virtual NullStmt * mutate( NullStmt * nullStmt ) override final;
 	virtual Statement * mutate( DeclStmt * declStmt ) override final;
@@ -296,4 +298,6 @@
 	void indexerAddUnionFwd ( UnionDecl           * node ) { indexer_impl_addUnionFwd ( pass, 0, node ); }
 	void indexerAddTrait    ( TraitDecl           * node ) { indexer_impl_addTrait    ( pass, 0, node ); }
+	void indexerAddWith     ( WithStmt            * node ) { indexer_impl_addWith    ( pass, 0, node ); }
+
 
 	template< typename TreeType, typename VisitorType >
Index: src/Common/PassVisitor.impl.h
===================================================================
--- src/Common/PassVisitor.impl.h	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/Common/PassVisitor.impl.h	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -985,4 +985,34 @@
 }
 
+
+
+//--------------------------------------------------------------------------
+// NullStmt
+template< typename pass_type >
+void PassVisitor< pass_type >::visit( WithStmt * node ) {
+	VISIT_START( node );
+	maybeAccept_impl( node->exprs, *this );
+	{
+		// catch statements introduce a level of scope (for the caught exception)
+		auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
+		indexerAddWith( node );
+		maybeAccept_impl( node->stmt, *this );
+	}
+	VISIT_END( node );
+}
+
+template< typename pass_type >
+Statement * PassVisitor< pass_type >::mutate( WithStmt * node ) {
+	MUTATE_START( node );
+	maybeMutate_impl( node->exprs, *this );
+	{
+		// catch statements introduce a level of scope (for the caught exception)
+		auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
+		indexerAddWith( node );
+		maybeMutate_impl( node->stmt, *this );
+	}
+	MUTATE_END( Statement, node );
+}
+
 //--------------------------------------------------------------------------
 // NullStmt
Index: src/Common/PassVisitor.proto.h
===================================================================
--- src/Common/PassVisitor.proto.h	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/Common/PassVisitor.proto.h	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -208,4 +208,5 @@
 INDEXER_FUNC( addUnion  , UnionDecl *           );
 INDEXER_FUNC( addTrait  , TraitDecl *           );
+INDEXER_FUNC( addWith   , WithStmt *            );
 
 
Index: src/GenPoly/InstantiateGeneric.cc
===================================================================
--- src/GenPoly/InstantiateGeneric.cc	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/GenPoly/InstantiateGeneric.cc	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -453,13 +453,4 @@
 			return false;
 		}
-
-		AggregateDecl * getAggr( Type * t ) {
-			if ( StructInstType * inst = dynamic_cast< StructInstType * >( t ) ) {
-				return inst->baseStruct;
-			} else if ( UnionInstType * inst = dynamic_cast< UnionInstType * >( t ) ) {
-				return inst->baseUnion;
-			}
-			assertf( false, "Non-aggregate type: %s", toString( t ).c_str() );
-		}
 	}
 
@@ -469,5 +460,5 @@
 		if ( isGenericType( memberExpr->aggregate->result ) ) {
 			// find the location of the member
-			AggregateDecl * aggr = getAggr( memberExpr->aggregate->result );
+			AggregateDecl * aggr = memberExpr->aggregate->result->getAggr();
 			std::list< Declaration * > & members = aggr->members;
 			memberIndex = std::distance( members.begin(), std::find( members.begin(), members.end(), memberExpr->member ) );
@@ -479,5 +470,5 @@
 		if ( memberIndex != -1 ) {
 			// using the location from the generic type, find the member in the instantiation and rebuild the member expression
-			AggregateDecl * aggr = getAggr( memberExpr->aggregate->result );
+			AggregateDecl * aggr = memberExpr->aggregate->result->getAggr();
 			assertf( memberIndex < (int)aggr->members.size(), "Instantiation somehow has fewer members than the generic type." );
 			Declaration * member = *std::next( aggr->members.begin(), memberIndex );
Index: src/Parser/DeclarationNode.cc
===================================================================
--- src/Parser/DeclarationNode.cc	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/Parser/DeclarationNode.cc	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -717,8 +717,19 @@
 }
 
-DeclarationNode * DeclarationNode::addFunctionBody( StatementNode * body ) {
+DeclarationNode * DeclarationNode::addFunctionBody( StatementNode * body, StatementNode * with ) {
 	assert( type );
 	assert( type->kind == TypeData::Function );
 	assert( ! type->function.body );
+	if ( with ) {
+		// convert
+		//  void f(S s) with (s) { x = 0; }
+		// to
+		//  void f(S s) { with(s) { x = 0; } }
+		WithStmt * withStmt = strict_dynamic_cast< WithStmt * >( with->build() );
+		withStmt->stmt = body->build();
+		delete body;
+		delete with;
+		body = new StatementNode( new CompoundStmt( { withStmt } ) );
+	}
 	type->function.body = body;
 	return this;
Index: src/Parser/ParseNode.h
===================================================================
--- src/Parser/ParseNode.h	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/Parser/ParseNode.h	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -69,5 +69,8 @@
 
 	virtual void print( __attribute__((unused)) std::ostream &os, __attribute__((unused)) int indent = 0 ) const {}
-	virtual void printList( __attribute__((unused)) std::ostream &os, __attribute__((unused)) int indent = 0 ) const {}
+	virtual void printList( std::ostream &os, int indent = 0 ) const {
+		print( os, indent );
+		if ( next ) next->print( os, indent );
+	}
 
 	static int indent_by;
@@ -120,5 +123,7 @@
 	ExpressionNode * set_extension( bool exten ) { extension = exten; return this; }
 
-	virtual void print( __attribute__((unused)) std::ostream &os, __attribute__((unused)) int indent = 0 ) const override {}
+	virtual void print( std::ostream &os, __attribute__((unused)) int indent = 0 ) const override {
+		os << expr.get() << std::endl;
+	}
 	void printOneLine( __attribute__((unused)) std::ostream &os, __attribute__((unused)) int indent = 0 ) const {}
 
@@ -257,5 +262,5 @@
 	DeclarationNode * addBitfield( ExpressionNode * size );
 	DeclarationNode * addVarArgs();
-	DeclarationNode * addFunctionBody( StatementNode * body );
+	DeclarationNode * addFunctionBody( StatementNode * body, StatementNode * with = nullptr );
 	DeclarationNode * addOldDeclList( DeclarationNode * list );
 	DeclarationNode * setBase( TypeData * newType );
@@ -359,6 +364,7 @@
 	virtual StatementNode * append_last_case( StatementNode * );
 
-	virtual void print( __attribute__((unused)) std::ostream &os, __attribute__((unused)) int indent = 0 ) const override {}
-	virtual void printList( __attribute__((unused)) std::ostream &os, __attribute__((unused)) int indent = 0 ) const override {}
+	virtual void print( std::ostream &os, __attribute__((unused)) int indent = 0 ) const override {
+		os << stmt.get() << std::endl;
+	}
   private:
 	std::unique_ptr<Statement> stmt;
@@ -408,4 +414,5 @@
 WaitForStmt * build_waitfor_timeout( ExpressionNode * timeout, StatementNode * stmt, ExpressionNode * when );
 WaitForStmt * build_waitfor_timeout( ExpressionNode * timeout, StatementNode * stmt, ExpressionNode * when, StatementNode * else_stmt, ExpressionNode * else_when );
+WithStmt * build_with( ExpressionNode * exprs, StatementNode * stmt );
 
 //##############################################################################
Index: src/Parser/StatementNode.cc
===================================================================
--- src/Parser/StatementNode.cc	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/Parser/StatementNode.cc	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -282,8 +282,15 @@
 	node->timeout.condition = notZeroExpr( maybeMoveBuild<Expression>( when ) );
 
-	node->orelse.statement = maybeMoveBuild<Statement >( else_stmt );
+	node->orelse.statement  = maybeMoveBuild<Statement >( else_stmt );
 	node->orelse.condition  = notZeroExpr( maybeMoveBuild<Expression>( else_when ) );
 
 	return node;
+}
+
+WithStmt * build_with( ExpressionNode * exprs, StatementNode * stmt ) {
+	std::list< Expression * > e;
+	buildMoveList( exprs, e );
+	Statement * s = maybeMoveBuild<Statement>( stmt );
+	return new WithStmt( e, s );
 }
 
Index: src/Parser/parser.yy
===================================================================
--- src/Parser/parser.yy	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/Parser/parser.yy	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -1058,5 +1058,7 @@
 with_statement:
 	WITH '(' tuple_expression_list ')' statement
-		{ throw SemanticError("With clause is currently unimplemented."); $$ = nullptr; } // FIX ME
+		{
+			$$ = new StatementNode( build_with( $3, $5 ) );
+		}
 	;
 
@@ -2410,5 +2412,5 @@
 		{ $$ = nullptr; }
 	| WITH '(' tuple_expression_list ')'
-		{ throw SemanticError("With clause is currently unimplemented."); $$ = nullptr; } // FIX ME
+		{ $$ = new StatementNode( build_with( $3, nullptr ) ); }
 	;
 
@@ -2420,5 +2422,5 @@
 			// Add the function body to the last identifier in the function definition list, i.e., foo3:
 			//   [const double] foo1(), foo2( int ), foo3( double ) { return 3.0; }
-			$1->get_last()->addFunctionBody( $3 );
+			$1->get_last()->addFunctionBody( $3, $2 );
 			$$ = $1;
 		}
@@ -2428,5 +2430,5 @@
 			typedefTable.addToEnclosingScope( TypedefTable::ID );
 			typedefTable.leaveScope();
-			$$ = $2->addFunctionBody( $4 )->addType( $1 );
+			$$ = $2->addFunctionBody( $4, $3 )->addType( $1 );
 		}
 		// handles default int return type, OBSOLESCENT (see 1)
@@ -2435,5 +2437,5 @@
 			typedefTable.addToEnclosingScope( TypedefTable::ID );
 			typedefTable.leaveScope();
-			$$ = $2->addFunctionBody( $4 )->addQualifiers( $1 );
+			$$ = $2->addFunctionBody( $4, $3 )->addQualifiers( $1 );
 		}
 		// handles default int return type, OBSOLESCENT (see 1)
@@ -2442,5 +2444,5 @@
 			typedefTable.addToEnclosingScope( TypedefTable::ID );
 			typedefTable.leaveScope();
-			$$ = $2->addFunctionBody( $4 )->addQualifiers( $1 );
+			$$ = $2->addFunctionBody( $4, $3 )->addQualifiers( $1 );
 		}
 		// handles default int return type, OBSOLESCENT (see 1)
@@ -2449,5 +2451,5 @@
 			typedefTable.addToEnclosingScope( TypedefTable::ID );
 			typedefTable.leaveScope();
-			$$ = $3->addFunctionBody( $5 )->addQualifiers( $2 )->addQualifiers( $1 );
+			$$ = $3->addFunctionBody( $5, $4 )->addQualifiers( $2 )->addQualifiers( $1 );
 		}
 
@@ -2458,5 +2460,5 @@
 			typedefTable.addToEnclosingScope( TypedefTable::ID );
 			typedefTable.leaveScope();
-			$$ = $2->addOldDeclList( $3 )->addFunctionBody( $5 )->addType( $1 );
+			$$ = $2->addOldDeclList( $3 )->addFunctionBody( $5, $4 )->addType( $1 );
 		}
 		// handles default int return type, OBSOLESCENT (see 1)
@@ -2465,5 +2467,5 @@
 			typedefTable.addToEnclosingScope( TypedefTable::ID );
 			typedefTable.leaveScope();
-			$$ = $2->addOldDeclList( $3 )->addFunctionBody( $5 )->addQualifiers( $1 );
+			$$ = $2->addOldDeclList( $3 )->addFunctionBody( $5, $4 )->addQualifiers( $1 );
 		}
 		// handles default int return type, OBSOLESCENT (see 1)
@@ -2472,5 +2474,5 @@
 			typedefTable.addToEnclosingScope( TypedefTable::ID );
 			typedefTable.leaveScope();
-			$$ = $2->addOldDeclList( $3 )->addFunctionBody( $5 )->addQualifiers( $1 );
+			$$ = $2->addOldDeclList( $3 )->addFunctionBody( $5, $4 )->addQualifiers( $1 );
 		}
 		// handles default int return type, OBSOLESCENT (see 1)
@@ -2479,5 +2481,5 @@
 			typedefTable.addToEnclosingScope( TypedefTable::ID );
 			typedefTable.leaveScope();
-			$$ = $3->addOldDeclList( $4 )->addFunctionBody( $6 )->addQualifiers( $2 )->addQualifiers( $1 );
+			$$ = $3->addOldDeclList( $4 )->addFunctionBody( $6, $5 )->addQualifiers( $2 )->addQualifiers( $1 );
 		}
 	;
Index: src/ResolvExpr/AlternativeFinder.cc
===================================================================
--- src/ResolvExpr/AlternativeFinder.cc	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/ResolvExpr/AlternativeFinder.cc	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -83,13 +83,13 @@
 	}
 
+	void printAlts( const AltList &list, std::ostream &os, unsigned int indentAmt ) {
+		Indenter indent = { Indenter::tabsize, indentAmt };
+		for ( AltList::const_iterator i = list.begin(); i != list.end(); ++i ) {
+			i->print( os, indent );
+			os << std::endl;
+		}
+	}
+
 	namespace {
-		void printAlts( const AltList &list, std::ostream &os, unsigned int indentAmt = 0 ) {
-			Indenter indent = { Indenter::tabsize, indentAmt };
-			for ( AltList::const_iterator i = list.begin(); i != list.end(); ++i ) {
-				i->print( os, indent );
-				os << std::endl;
-			}
-		}
-
 		void makeExprList( const AltList &in, std::list< Expression* > &out ) {
 			for ( AltList::const_iterator i = in.begin(); i != in.end(); ++i ) {
@@ -469,11 +469,12 @@
 			std::cerr << std::endl;
 		)
-		std::list< DeclarationWithType* > candidates;
+		std::list< SymTab::Indexer::IdData > candidates;
 		decls.lookupId( curDecl->get_name(), candidates );
 ///   if ( candidates.empty() ) { std::cerr << "no candidates!" << std::endl; }
-		for ( std::list< DeclarationWithType* >::const_iterator candidate = candidates.begin(); candidate != candidates.end(); ++candidate ) {
+		for ( const auto & data : candidates ) {
+			DeclarationWithType * candidate = data.id;
 			PRINT(
 				std::cerr << "inferRecursive: candidate is ";
-				(*candidate)->print( std::cerr );
+				candidate->print( std::cerr );
 				std::cerr << std::endl;
 			)
@@ -482,5 +483,5 @@
 			TypeEnvironment newEnv( newAlt.env );
 			OpenVarSet newOpenVars( openVars );
-			Type *adjType = (*candidate)->get_type()->clone();
+			Type *adjType = candidate->get_type()->clone();
 			adjustExprType( adjType, newEnv, indexer );
 			adjType->accept( global_renamer );
@@ -500,6 +501,5 @@
 				Alternative newerAlt( newAlt );
 				newerAlt.env = newEnv;
-				assertf( (*candidate)->get_uniqueId(), "Assertion candidate does not have a unique ID: %s", toString( *candidate ).c_str() );
-				DeclarationWithType *candDecl = static_cast< DeclarationWithType* >( Declaration::declFromId( (*candidate)->get_uniqueId() ) );
+				assertf( candidate->get_uniqueId(), "Assertion candidate does not have a unique ID: %s", toString( candidate ).c_str() );
 
 				// everything with an empty idChain was pulled in by the current assertion.
@@ -516,5 +516,5 @@
 				// DOESN'T WORK: grandchild nodes conflict with their cousins
 				//if ( newNeedParents[ curDecl->get_uniqueId() ][ candDecl->get_uniqueId() ]++ > recursionParentLimit ) continue;
-				Expression *varExpr = new VariableExpr( candDecl );
+				Expression *varExpr = data.combine();
 				delete varExpr->get_result();
 				varExpr->set_result( adjType->clone() );
@@ -522,6 +522,6 @@
 					std::cerr << "satisfying assertion " << curDecl->get_uniqueId() << " ";
 					curDecl->print( std::cerr );
-					std::cerr << " with declaration " << (*candidate)->get_uniqueId() << " ";
-					(*candidate)->print( std::cerr );
+					std::cerr << " with declaration " << candidate->get_uniqueId() << " ";
+					candidate->print( std::cerr );
 					std::cerr << std::endl;
 				)
@@ -532,5 +532,5 @@
 				}
 				// XXX: this is a memory leak, but adjType can't be deleted because it might contain assertions
-				(*inferParameters)[ curDecl->get_uniqueId() ] = ParamEntry( (*candidate)->get_uniqueId(), adjType->clone(), curDecl->get_type()->clone(), varExpr );
+				(*inferParameters)[ curDecl->get_uniqueId() ] = ParamEntry( candidate->get_uniqueId(), adjType->clone(), curDecl->get_type()->clone(), varExpr );
 				inferRecursive( begin, end, newerAlt, newOpenVars, newDecls, newerNeed, /*newNeedParents,*/ level, indexer, out );
 			} else {
@@ -1317,16 +1317,17 @@
 
 	void AlternativeFinder::visit( NameExpr *nameExpr ) {
-		std::list< DeclarationWithType* > declList;
+		std::list< SymTab::Indexer::IdData > declList;
 		indexer.lookupId( nameExpr->get_name(), declList );
 		PRINT( std::cerr << "nameExpr is " << nameExpr->get_name() << std::endl; )
-		for ( std::list< DeclarationWithType* >::iterator i = declList.begin(); i != declList.end(); ++i ) {
-			VariableExpr newExpr( *i );
-			alternatives.push_back( Alternative( newExpr.clone(), env, Cost::zero ) );
+		for ( auto & data : declList ) {
+			Expression * newExpr = data.combine();
+			// xxx - add in extra cost for with-statement exprs?
+			alternatives.push_back( Alternative( newExpr, env, Cost::zero ) );
 			PRINT(
 				std::cerr << "decl is ";
-				(*i)->print( std::cerr );
+				data.id->print( std::cerr );
 				std::cerr << std::endl;
 				std::cerr << "newExpr is ";
-				newExpr.print( std::cerr );
+				newExpr->print( std::cerr );
 				std::cerr << std::endl;
 			)
@@ -1420,21 +1421,25 @@
 	}
 
-	void AlternativeFinder::resolveAttr( DeclarationWithType *funcDecl, FunctionType *function, Type *argType, const TypeEnvironment &env ) {
-		// assume no polymorphism
-		// assume no implicit conversions
-		assert( function->get_parameters().size() == 1 );
-		PRINT(
-			std::cerr << "resolvAttr: funcDecl is ";
-			funcDecl->print( std::cerr );
-			std::cerr << " argType is ";
-			argType->print( std::cerr );
-			std::cerr << std::endl;
-		)
-		if ( typesCompatibleIgnoreQualifiers( argType, function->get_parameters().front()->get_type(), indexer, env ) ) {
-			alternatives.push_back( Alternative( new AttrExpr( new VariableExpr( funcDecl ), argType->clone() ), env, Cost::zero ) );
-			for ( std::list< DeclarationWithType* >::iterator i = function->get_returnVals().begin(); i != function->get_returnVals().end(); ++i ) {
-				alternatives.back().expr->set_result( (*i)->get_type()->clone() );
-			} // for
-		} // if
+	namespace {
+		void resolveAttr( SymTab::Indexer::IdData data, FunctionType *function, Type *argType, const TypeEnvironment &env, AlternativeFinder & finder ) {
+			// assume no polymorphism
+			// assume no implicit conversions
+			assert( function->get_parameters().size() == 1 );
+			PRINT(
+				std::cerr << "resolvAttr: funcDecl is ";
+				data.id->print( std::cerr );
+				std::cerr << " argType is ";
+				argType->print( std::cerr );
+				std::cerr << std::endl;
+			)
+			const SymTab::Indexer & indexer = finder.get_indexer();
+			AltList & alternatives = finder.get_alternatives();
+			if ( typesCompatibleIgnoreQualifiers( argType, function->get_parameters().front()->get_type(), indexer, env ) ) {
+				alternatives.push_back( Alternative( new AttrExpr( data.combine(), argType->clone() ), env, Cost::zero ) );
+				for ( DeclarationWithType * retVal : function->returnVals ) {
+					alternatives.back().expr->result = retVal->get_type()->clone();
+				} // for
+			} // if
+		}
 	}
 
@@ -1443,14 +1448,15 @@
 		NameExpr *nameExpr = dynamic_cast< NameExpr* >( attrExpr->get_attr() );
 		assert( nameExpr );
-		std::list< DeclarationWithType* > attrList;
+		std::list< SymTab::Indexer::IdData > attrList;
 		indexer.lookupId( nameExpr->get_name(), attrList );
 		if ( attrExpr->get_isType() || attrExpr->get_expr() ) {
-			for ( std::list< DeclarationWithType* >::iterator i = attrList.begin(); i != attrList.end(); ++i ) {
+			for ( auto & data : attrList ) {
+				DeclarationWithType * id = data.id;
 				// check if the type is function
-				if ( FunctionType *function = dynamic_cast< FunctionType* >( (*i)->get_type() ) ) {
+				if ( FunctionType *function = dynamic_cast< FunctionType* >( id->get_type() ) ) {
 					// assume exactly one parameter
 					if ( function->get_parameters().size() == 1 ) {
 						if ( attrExpr->get_isType() ) {
-							resolveAttr( *i, function, attrExpr->get_type(), env );
+							resolveAttr( data, function, attrExpr->get_type(), env, *this );
 						} else {
 							AlternativeFinder finder( indexer, env );
@@ -1458,5 +1464,5 @@
 							for ( AltList::iterator choice = finder.alternatives.begin(); choice != finder.alternatives.end(); ++choice ) {
 								if ( choice->expr->get_result()->size() == 1 ) {
-									resolveAttr(*i, function, choice->expr->get_result(), choice->env );
+									resolveAttr(data, function, choice->expr->get_result(), choice->env, *this );
 								} // fi
 							} // for
@@ -1466,7 +1472,6 @@
 			} // for
 		} else {
-			for ( std::list< DeclarationWithType* >::iterator i = attrList.begin(); i != attrList.end(); ++i ) {
-				VariableExpr newExpr( *i );
-				alternatives.push_back( Alternative( newExpr.clone(), env, Cost::zero ) );
+			for ( auto & data : attrList ) {
+				alternatives.push_back( Alternative( data.combine(), env, Cost::zero ) );
 				renameTypes( alternatives.back().expr );
 			} // for
Index: src/ResolvExpr/AlternativeFinder.h
===================================================================
--- src/ResolvExpr/AlternativeFinder.h	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/ResolvExpr/AlternativeFinder.h	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -142,5 +142,4 @@
 		template< typename OutputIterator >
 		void inferParameters( const AssertionSet &need, AssertionSet &have, const Alternative &newAlt, OpenVarSet &openVars, OutputIterator out );
-		void resolveAttr( DeclarationWithType *funcDecl, FunctionType *function, Type *argType, const TypeEnvironment &env );
 
 		const SymTab::Indexer &indexer;
@@ -151,5 +150,4 @@
 
 	Expression *resolveInVoidContext( Expression *expr, const SymTab::Indexer &indexer, TypeEnvironment &env );
-	void referenceToRvalueConversion( Expression *& expr );
 
 	template< typename InputIterator, typename OutputIterator >
@@ -174,4 +172,5 @@
 
 	Cost sumCost( const AltList &in );
+	void printAlts( const AltList &list, std::ostream &os, unsigned int indentAmt = 0 );
 
 	template< typename InputIterator >
@@ -181,4 +180,5 @@
 		}
 	}
+
 } // namespace ResolvExpr
 
Index: src/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/ResolvExpr/Resolver.cc	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -26,4 +26,5 @@
 #include "Common/utility.h"              // for ValueGuard, group_iterate
 #include "CurrentObject.h"               // for CurrentObject
+#include "InitTweak/GenInit.h"
 #include "InitTweak/InitTweak.h"         // for isIntrinsicSingleArgCallStmt
 #include "RenameVars.h"                  // for RenameVars, global_renamer
@@ -40,4 +41,5 @@
 #include "SynTree/TypeSubstitution.h"    // for TypeSubstitution
 #include "SynTree/Visitor.h"             // for acceptAll, maybeAccept
+#include "Tuples/Tuples.h"
 #include "typeops.h"                     // for extractResultType
 #include "Unify.h"                       // for unify
@@ -46,5 +48,5 @@
 
 namespace ResolvExpr {
-	struct Resolver final : public WithIndexer, public WithGuards, public WithVisitorRef<Resolver>, public WithShortCircuiting {
+	struct Resolver final : public WithIndexer, public WithGuards, public WithVisitorRef<Resolver>, public WithShortCircuiting, public WithStmtsToAdd {
 		Resolver() {}
 		Resolver( const SymTab::Indexer & other ) {
@@ -74,4 +76,5 @@
 		void previsit( CatchStmt *catchStmt );
 		void previsit( WaitForStmt * stmt );
+		void previsit( WithStmt * withStmt );
 
 		void previsit( SingleInit *singleInit );
@@ -571,4 +574,59 @@
 	}
 
+	bool isStructOrUnion( Type * t ) {
+		t = t->stripReferences();
+		return dynamic_cast< StructInstType * >( t ) || dynamic_cast< UnionInstType * >( t );
+	}
+
+	void Resolver::previsit( WithStmt * withStmt ) {
+		for ( Expression *& expr : withStmt->exprs )  {
+			TypeEnvironment env;
+			AlternativeFinder finder( indexer, env );
+			finder.findWithAdjustment( expr );
+
+			// only struct- and union-typed expressions are viable candidates
+			AltList candidates;
+			for ( Alternative & alt : finder.get_alternatives() ) {
+				if ( isStructOrUnion( alt.expr->result ) ) {
+					candidates.push_back( std::move( alt ) );
+				}
+			}
+
+			// choose the lowest cost expression among the candidates
+			AltList winners;
+			findMinCost( candidates.begin(), candidates.end(), back_inserter( winners ) );
+			if ( winners.size() == 0 ) {
+				throw SemanticError( "No reasonable alternatives for with statement expression: ", expr );
+			} else if ( winners.size() != 1 ) {
+				std::ostringstream stream;
+				stream << "Cannot choose between " << winners.size() << " alternatives for with statement expression\n";
+				expr->print( stream );
+				stream << "Alternatives are:\n";
+				printAlts( winners, stream, 1 );
+				throw SemanticError( stream.str() );
+			}
+
+			// there is one unambiguous interpretation - move the expression into the with statement
+			Alternative & alt = winners.front();
+			finishExpr( alt.expr, alt.env, expr->env );
+			delete expr;
+			expr = alt.expr;
+			alt.expr = nullptr;
+
+			// if with expression might be impure, create a temporary so that it is evaluated once
+			if ( Tuples::maybeImpure( expr ) ) {
+				static UniqueName tmpNamer( "_with_tmp_" );
+				ObjectDecl * tmp = ObjectDecl::newObject( tmpNamer.newName(), expr->result->clone(), new SingleInit( expr ) );
+				expr = new VariableExpr( tmp );
+				stmtsToAddBefore.push_back( new DeclStmt( tmp ) );
+				if ( InitTweak::isConstructable( tmp->type ) ) {
+					// generate ctor/dtor and resolve them
+					tmp->init = InitTweak::genCtorInit( tmp );
+					tmp->accept( *visitor );
+				}
+			}
+		}
+	}
+
 	template< typename T >
 	bool isCharType( T t ) {
Index: src/ResolvExpr/typeops.h
===================================================================
--- src/ResolvExpr/typeops.h	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/ResolvExpr/typeops.h	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -102,4 +102,7 @@
 	bool occurs( Type *type, std::string varName, const TypeEnvironment &env );
 
+	// in AlternativeFinder.cc
+	void referenceToRvalueConversion( Expression *& expr );
+
 	// flatten tuple type into list of types
 	template< typename OutputIterator >
Index: src/SymTab/Indexer.cc
===================================================================
--- src/SymTab/Indexer.cc	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/SymTab/Indexer.cc	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -40,5 +40,9 @@
 
 namespace SymTab {
-	typedef std::unordered_map< std::string, DeclarationWithType* > MangleTable;
+	std::ostream & operator<<( std::ostream & out, const Indexer::IdData & data ) {
+		return out << "(" << data.id << "," << data.baseExpr << ")";
+	}
+
+	typedef std::unordered_map< std::string, Indexer::IdData > MangleTable;
 	typedef std::unordered_map< std::string, MangleTable > IdTable;
 	typedef std::unordered_map< std::string, NamedTypeDecl* > TypeTable;
@@ -97,5 +101,5 @@
 	}
 
-	void Indexer::removeSpecialOverrides( const std::string &id, std::list< DeclarationWithType * > & out ) const {
+	void Indexer::removeSpecialOverrides( const std::string &id, std::list< IdData > & out ) const {
 		// only need to perform this step for constructors, destructors, and assignment functions
 		if ( ! CodeGen::isCtorDtorAssign( id ) ) return;
@@ -104,5 +108,5 @@
 		struct ValueType {
 			struct DeclBall {
-				FunctionDecl * decl;
+				IdData decl;
 				bool isUserDefinedFunc; // properties for this particular decl
 				bool isDefaultCtor;
@@ -120,10 +124,11 @@
 			// another FunctionDecl for the current type was found - determine
 			// if it has special properties and update data structure accordingly
-			ValueType & operator+=( FunctionDecl * function ) {
+			ValueType & operator+=( IdData data ) {
+				DeclarationWithType * function = data.id;
 				bool isUserDefinedFunc = ! LinkageSpec::isOverridable( function->get_linkage() );
 				bool isDefaultCtor = InitTweak::isDefaultConstructor( function );
 				bool isDtor = InitTweak::isDestructor( function );
 				bool isCopyFunc = InitTweak::isCopyFunction( function, function->get_name() );
-				decls.push_back( DeclBall{ function, isUserDefinedFunc, isDefaultCtor, isDtor, isCopyFunc } );
+				decls.push_back( DeclBall{ data, isUserDefinedFunc, isDefaultCtor, isDtor, isCopyFunc } );
 				existsUserDefinedFunc = existsUserDefinedFunc || isUserDefinedFunc;
 				existsUserDefinedCtor = existsUserDefinedCtor || (isUserDefinedFunc && CodeGen::isConstructor( function->get_name() ) );
@@ -135,11 +140,11 @@
 		}; // ValueType
 
-		std::list< DeclarationWithType * > copy;
+		std::list< IdData > copy;
 		copy.splice( copy.end(), out );
 
 		// organize discovered declarations by type
 		std::unordered_map< std::string, ValueType > funcMap;
-		for ( DeclarationWithType * decl : copy ) {
-			if ( FunctionDecl * function = dynamic_cast< FunctionDecl * >( decl ) ) {
+		for ( auto decl : copy ) {
+			if ( FunctionDecl * function = dynamic_cast< FunctionDecl * >( decl.id ) ) {
 				std::list< DeclarationWithType * > & params = function->get_functionType()->get_parameters();
 				assert( ! params.empty() );
@@ -147,5 +152,5 @@
 				Type * base = InitTweak::getPointerBase( params.front()->get_type() );
 				assert( base );
-				funcMap[ Mangler::mangle( base ) ] += function;
+				funcMap[ Mangler::mangle( base ) ] += decl;
 			} else {
 				out.push_back( decl );
@@ -164,5 +169,5 @@
 				bool noUserDefinedFunc = ! val.existsUserDefinedFunc;
 				bool isUserDefinedFunc = ball.isUserDefinedFunc;
-				bool isAcceptableDefaultCtor = (! val.existsUserDefinedCtor || (! val.existsUserDefinedDefaultCtor && ball.decl->get_linkage() == LinkageSpec::Intrinsic)) && ball.isDefaultCtor; // allow default constructors only when no user-defined constructors exist, except in the case of intrinsics, which require exact overrides
+				bool isAcceptableDefaultCtor = (! val.existsUserDefinedCtor || (! val.existsUserDefinedDefaultCtor && ball.decl.id->get_linkage() == LinkageSpec::Intrinsic)) && ball.isDefaultCtor; // allow default constructors only when no user-defined constructors exist, except in the case of intrinsics, which require exact overrides
 				bool isAcceptableCopyFunc = ! val.existsUserDefinedCopyFunc && ball.isCopyFunc; // handles copy ctor and assignment operator
 				bool isAcceptableDtor = ! val.existsUserDefinedDtor && ball.isDtor;
@@ -219,5 +224,5 @@
 	}
 
-	void Indexer::lookupId( const std::string &id, std::list< DeclarationWithType* > &out ) const {
+	void Indexer::lookupId( const std::string &id, std::list< IdData > &out ) const {
 		std::unordered_set< std::string > foundMangleNames;
 
@@ -289,5 +294,5 @@
 			const MangleTable &mangleTable = decls->second;
 			MangleTable::const_iterator decl = mangleTable.find( mangleName );
-			if ( decl != mangleTable.end() ) return decl->second;
+			if ( decl != mangleTable.end() ) return decl->second.id;
 		}
 
@@ -304,5 +309,5 @@
 			for ( MangleTable::const_iterator decl = mangleTable.begin(); decl != mangleTable.end(); ++decl ) {
 				// check for C decls with the same name, skipping those with a compatible type (by mangleName)
-				if ( ! LinkageSpec::isMangled( decl->second->get_linkage() ) && decl->first != mangleName ) return true;
+				if ( ! LinkageSpec::isMangled( decl->second.id->get_linkage() ) && decl->first != mangleName ) return true;
 			}
 		}
@@ -321,5 +326,5 @@
 				// check for C decls with the same name, skipping
 				// those with an incompatible type (by mangleName)
-				if ( ! LinkageSpec::isMangled( decl->second->get_linkage() ) && decl->first == mangleName ) return true;
+				if ( ! LinkageSpec::isMangled( decl->second.id->get_linkage() ) && decl->first == mangleName ) return true;
 			}
 		}
@@ -403,5 +408,5 @@
 	}
 
-	void Indexer::addId( DeclarationWithType *decl ) {
+	void Indexer::addId( DeclarationWithType *decl, Expression * baseExpr ) {
 		debugPrint( "Adding Id " << decl->name << std::endl );
 		makeWritable();
@@ -439,5 +444,5 @@
 
 		// add to indexer
-		tables->idTable[ name ][ mangleName ] = decl;
+		tables->idTable[ name ][ mangleName ] = { decl, baseExpr };
 		++tables->size;
 	}
@@ -563,4 +568,19 @@
 			if ( ! addedDeclConflicts( existing->second, decl ) ) {
 				existing->second = decl;
+			}
+		}
+	}
+
+	void Indexer::addWith( WithStmt * stmt ) {
+		for ( Expression * expr : stmt->exprs ) {
+			if ( expr->result ) {
+				AggregateDecl * aggr = expr->result->stripReferences()->getAggr();
+				assertf( aggr, "WithStmt expr has non-aggregate type: %s", toString( expr->result ).c_str() );
+
+				for ( Declaration * decl : aggr->members ) {
+					if ( DeclarationWithType * dwt = dynamic_cast< DeclarationWithType * >( decl ) ) {
+						addId( dwt, expr );
+					}
+				}
 			}
 		}
@@ -645,4 +665,19 @@
 
 	}
+
+	Expression * Indexer::IdData::combine() const {
+		if ( baseExpr ) {
+			Expression * base = baseExpr->clone();
+			ResolvExpr::referenceToRvalueConversion( base );
+			Expression * ret = new MemberExpr( id, base );
+			// xxx - this introduces hidden environments, for now remove them.
+			// std::swap( base->env, ret->env );
+			delete base->env;
+			base->env = nullptr;
+			return ret;
+		} else {
+			return new VariableExpr( id );
+		}
+	}
 } // namespace SymTab
 
Index: src/SymTab/Indexer.h
===================================================================
--- src/SymTab/Indexer.h	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/SymTab/Indexer.h	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -39,6 +39,13 @@
 		void leaveScope();
 
+		struct IdData {
+			DeclarationWithType * id;
+			Expression * baseExpr; // WithExpr
+
+			Expression * combine() const;
+		};
+
 		/// Gets all declarations with the given ID
-		void lookupId( const std::string &id, std::list< DeclarationWithType* > &out ) const;
+		void lookupId( const std::string &id, std::list< IdData > &out ) const;
 		/// Gets the top-most type declaration with the given ID
 		NamedTypeDecl *lookupType( const std::string &id ) const;
@@ -67,5 +74,5 @@
 		TraitDecl *lookupTraitAtScope( const std::string &id, unsigned long scope ) const;
 
-		void addId( DeclarationWithType *decl );
+		void addId( DeclarationWithType *decl, Expression * baseExpr = nullptr );
 		void addType( NamedTypeDecl *decl );
 		void addStruct( const std::string &id );
@@ -75,4 +82,7 @@
 		void addUnion( UnionDecl *decl );
 		void addTrait( TraitDecl *decl );
+
+		/// adds all of the IDs from WithStmt exprs
+		void addWith( WithStmt * );
 
 		/// convenience function for adding a list of Ids to the indexer
@@ -100,5 +110,5 @@
 		// so that they will not be selected
 		// void removeSpecialOverrides( FunctionDecl *decl );
-		void removeSpecialOverrides( const std::string &id, std::list< DeclarationWithType * > & out ) const;
+		void removeSpecialOverrides( const std::string &id, std::list< IdData > & out ) const;
 
 		/// Ensures that tables variable is writable (i.e. allocated, uniquely owned by this Indexer, and at the current scope)
Index: src/SynTree/Mutator.cc
===================================================================
--- src/SynTree/Mutator.cc	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/SynTree/Mutator.cc	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -203,4 +203,10 @@
 }
 
+Statement * Mutator::mutate( WithStmt * withStmt ) {
+	mutateAll( withStmt->exprs, *this );
+	withStmt->stmt = maybeMutate( withStmt->stmt, *this );
+	return withStmt;
+}
+
 NullStmt * Mutator::mutate( NullStmt *nullStmt ) {
 	return nullStmt;
Index: src/SynTree/Mutator.h
===================================================================
--- src/SynTree/Mutator.h	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/SynTree/Mutator.h	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -50,4 +50,5 @@
 	virtual Statement * mutate( FinallyStmt * catchStmt );
 	virtual Statement * mutate( WaitForStmt * waitforStmt );
+	virtual Statement * mutate( WithStmt * withStmt );
 	virtual NullStmt * mutate( NullStmt * nullStmt );
 	virtual Statement * mutate( DeclStmt * declStmt );
Index: src/SynTree/ReferenceToType.cc
===================================================================
--- src/SynTree/ReferenceToType.cc	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/SynTree/ReferenceToType.cc	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -70,4 +70,6 @@
 bool StructInstType::isComplete() const { return baseStruct ? baseStruct->has_body() : false; }
 
+AggregateDecl * StructInstType::getAggr() { return baseStruct; }
+
 void StructInstType::lookup( const std::string &name, std::list< Declaration* > &foundDecls ) const {
 	assert( baseStruct );
@@ -101,4 +103,6 @@
 
 bool UnionInstType::isComplete() const { return baseUnion ? baseUnion->has_body() : false; }
+
+AggregateDecl * UnionInstType::getAggr() { return baseUnion; }
 
 void UnionInstType::lookup( const std::string &name, std::list< Declaration* > &foundDecls ) const {
Index: src/SynTree/Statement.cc
===================================================================
--- src/SynTree/Statement.cc	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/SynTree/Statement.cc	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -456,4 +456,21 @@
 }
 
+
+WithStmt::WithStmt( const std::list< Expression * > & exprs, Statement * stmt ) : Statement(), exprs( exprs ), stmt( stmt ) {}
+WithStmt::WithStmt( const WithStmt & other ) : Statement( other ), stmt( maybeClone( other.stmt ) ) {
+	cloneAll( other.exprs, exprs );
+}
+WithStmt::~WithStmt() {
+	deleteAll( exprs );
+	delete stmt;
+}
+
+void WithStmt::print( std::ostream & os, Indenter indent ) const {
+	os << "With statement" << endl;
+	os << indent << "... with statement:" << endl << indent+1;
+	stmt->print( os, indent+1 );
+}
+
+
 NullStmt::NullStmt( const std::list<Label> & labels ) : Statement( labels ) {
 }
Index: src/SynTree/Statement.h
===================================================================
--- src/SynTree/Statement.h	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/SynTree/Statement.h	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -431,4 +431,19 @@
 };
 
+class WithStmt : public Statement {
+public:
+	std::list< Expression * > exprs;
+	Statement * stmt;
+
+	WithStmt( const std::list< Expression * > & exprs, Statement * stmt );
+	WithStmt( const WithStmt & other );
+	virtual ~WithStmt();
+
+	virtual WithStmt * clone() const override { return new WithStmt( *this ); }
+	virtual void accept( Visitor & v ) override { v.visit( this ); }
+	virtual Statement * acceptMutator( Mutator & m )  override { return m.mutate( this ); }
+	virtual void print( std::ostream & os, Indenter indent = {} ) const override;
+};
+
 
 // represents a declaration that occurs as part of a compound statement
Index: src/SynTree/SynTree.h
===================================================================
--- src/SynTree/SynTree.h	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/SynTree/SynTree.h	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -55,4 +55,5 @@
 class FinallyStmt;
 class WaitForStmt;
+class WithStmt;
 class NullStmt;
 class DeclStmt;
Index: src/SynTree/Type.h
===================================================================
--- src/SynTree/Type.h	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/SynTree/Type.h	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -178,4 +178,6 @@
 	virtual bool isComplete() const { return true; }
 
+	virtual AggregateDecl * getAggr() {	assertf( false, "Non-aggregate type: %s", toString( this ).c_str() ); }
+
 	virtual Type *clone() const = 0;
 	virtual void accept( Visitor & v ) = 0;
@@ -405,4 +407,6 @@
 	virtual bool isComplete() const override;
 
+	virtual AggregateDecl * getAggr() override;
+
 	/// Looks up the members of this struct named "name" and places them into "foundDecls".
 	/// Clones declarations into "foundDecls", caller responsible for freeing
@@ -436,4 +440,6 @@
 
 	virtual bool isComplete() const override;
+
+	virtual AggregateDecl * getAggr() override;
 
 	/// looks up the members of this union named "name" and places them into "foundDecls"
Index: src/SynTree/Visitor.cc
===================================================================
--- src/SynTree/Visitor.cc	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/SynTree/Visitor.cc	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -174,5 +174,10 @@
 }
 
-void Visitor::visit( __attribute__((unused)) NullStmt *nullStmt ) {
+void Visitor::visit( WithStmt * withStmt ) {
+	acceptAll( withStmt->exprs, *this );
+	maybeAccept( withStmt->stmt, *this );
+}
+
+void Visitor::visit( NullStmt * ) {
 }
 
Index: src/SynTree/Visitor.h
===================================================================
--- src/SynTree/Visitor.h	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/SynTree/Visitor.h	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -52,4 +52,5 @@
 	virtual void visit( FinallyStmt * finallyStmt );
 	virtual void visit( WaitForStmt * waitforStmt );
+	virtual void visit( WithStmt * withStmt );
 	virtual void visit( NullStmt * nullStmt );
 	virtual void visit( DeclStmt * declStmt );
Index: src/tests/.expect/declarationErrors.txt
===================================================================
--- src/tests/.expect/declarationErrors.txt	(revision 12d2dc8fad3691528e0f541fe5bd38046cdc9877)
+++ src/tests/.expect/declarationErrors.txt	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -7,4 +7,5 @@
 declarationErrors.c:19:1 error: duplicate static in declaration of x4: static const volatile instance of const volatile struct __anonymous0
   with members 
+    i: int 
    with body 
 
@@ -12,4 +13,5 @@
 declarationErrors.c:20:1 error: duplicate const, duplicate static, duplicate volatile in declaration of x5: static const volatile instance of const volatile struct __anonymous1
   with members 
+    i: int 
    with body 
 
Index: src/tests/.expect/with-statement.txt
===================================================================
--- src/tests/.expect/with-statement.txt	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
+++ src/tests/.expect/with-statement.txt	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -0,0 +1,6 @@
+12345 12345 12345
+12345
+called mk
+444 444 444
+123456789 123456789 123456789
+123456789 123456789 123456789
Index: src/tests/with-statement.c
===================================================================
--- src/tests/with-statement.c	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
+++ src/tests/with-statement.c	(revision 65197c2ce4425076f94848e4be37218f1bcc0626)
@@ -0,0 +1,108 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2017 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// tupleFunction.c --
+//
+// Author           : Rob Schluntz
+// Created On       : Mon Dec 04 17:41:45 2017
+// Last Modified By : Rob Schluntz
+// Last Modified On : Mon Dec 04 17:45:07 2017
+// Update Count     : 2
+//
+
+struct S {
+  int i;
+  // dynamically allocated member ensures ctor/dtors are called correctly on temporaries
+  int * ptr;
+};
+
+// with clause on reference parameter
+void ?{}(S & this, int n) with(this) {
+  i = n;
+  ptr = (int *)malloc(sizeof(int));
+}
+
+void ?{}(S & this) {
+  this{ 0 };
+}
+
+void ?{}(S & this, S other) {
+  this{ other.i };
+}
+
+S ?=?(S & this, S other) with(this) {
+  i = other.i;
+  *ptr = *other.ptr;
+  return this;
+}
+
+void ^?{}(S & this) with(this) {
+  free(ptr);
+}
+
+struct S2 {
+  S s;
+};
+
+void ?{}(S2 & this, int n) {
+  (this.s){ n };
+}
+
+forall(otype T)
+struct Box {
+  T x;
+};
+
+forall(otype T)
+void ?{}(Box(T) & this) with(this) { // with clause in polymorphic function
+  x{};
+}
+
+void print(int i) { printf("%d", i); }
+
+forall(otype T | { void print(T); })
+void foo(T t) {
+  Box(T) b = { t };
+  with(b) {  // with statement in polymorphic function
+    print(x);
+    printf("\n");
+  }
+}
+
+// ensure with-statement temporary generation works correctly
+S mk() {
+  printf("called mk\n");
+  return (S) { 444 };
+}
+
+// ensure with-statement temporary generation with reference-returning functions works correctly
+S & ref() {
+  static S var = { 123456789 };
+  return var;
+}
+
+int main() {
+  S2 s2 = { 12345 };
+  with (s2) {
+    with(s) { // with s2.s
+      printf("%d %d %d\n", i, s.i, s2.s.i);
+      foo(i);  // s.i
+      with(mk()) {
+        printf("%d %d %d\n", i, i, i);
+        with(ref()) {
+          printf("%d %d %d\n", i, i, i);
+        } // with ref()
+        with(ref()) {
+          printf("%d %d %d\n", i, i, i);
+        } // with ref()
+      } // with mk()
+    } // with s
+  } // with s2
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// End: //
