Index: typemanager.cs
===================================================================
--- typemanager.cs	(revision 148775)
+++ typemanager.cs	(working copy)
@@ -30,7 +30,7 @@
 
 namespace Mono.CSharp {
 
-	partial class TypeManager {
+partial class TypeManager {
 	//
 	// A list of core types that the compiler requires or uses
 	//
@@ -103,6 +103,11 @@
 	public static TypeExpr binder_type;
 	public static Type binder_flags;
 
+	//
+	// Experimental
+	//
+	public static Type [] tuple_types;
+	
 	// 
 	// Expressions representing the internal types.  Used during declaration
 	// definition.
@@ -1022,6 +1027,10 @@
 		// Note: extension_attribute_type is already loaded
 		expression_type = CoreLookupType (ctx, "System.Linq.Expressions", "Expression`1", Kind.Class, false);
 
+		tuple_types = new Type [7];
+		for (int i = 2; i <= 8; i++)
+			tuple_types [i-2] = CoreLookupType (ctx, "System", "Tuple`" + i, Kind.Class, false);
+		
 		if (!RootContext.StdLib) {
 			//
 			// HACK: When building Mono corlib mcs uses loaded mscorlib which
@@ -1053,6 +1062,15 @@
 		}
 	}
 
+	public static int GetTupleArity (Type t)
+	{
+		t = DropGenericTypeArguments (t);
+		for (int i = 0; i < tuple_types.Length; i++)
+			if (t == tuple_types [i])
+				return i+2;
+		return 0;
+	}
+	
 	const BindingFlags instance_and_static = BindingFlags.Static | BindingFlags.Instance;
 
 	/// <remarks>
Index: Makefile
===================================================================
--- Makefile	(revision 148775)
+++ Makefile	(working copy)
@@ -119,4 +119,7 @@
 	echo -e 'var a = new int[]{1,2,3};\nfrom x in a select x;' | mono csharp.exe
 	echo -e 'var a = from f in System.IO.Directory.GetFiles ("/tmp") where f == "passwd" select f;' | mono csharp.exe
 
+qh: cs-parser.cs
+	MONO_PATH="./../class/lib/net_4_0_bootstrap:$MONO_PATH" /cvs/mono/runtime/mono-wrapper  ./../class/lib/net_4_0_bootstrap/dmcs.exe /codepage:65001  -optimize -d:NET_1_1 -d:NET_2_0 -d:NET_3_0 -d:NET_3_5 -d:NET_4_0 -debug -target:exe -out:dmcs.exe cs-parser.cs  @dmcs.exe.sources
 
+
Index: expression.cs
===================================================================
--- expression.cs	(revision 148775)
+++ expression.cs	(working copy)
@@ -9954,4 +9954,103 @@
 				Name, initializer);
 		}
 	}
+
+	public class TupleExpression : Expression, IAssignMethod {
+		List<Expression> expressions;
+		Expression [] accessors;
+		LocalTemporary local;
+		
+		public TupleExpression (Expression expr) 
+		{
+			loc = expr.Location;
+			expressions = new List<Expression> (4);
+			expressions.Add (expr);
+		}
+
+		public void Add (Expression expr)
+		{
+			expressions.Add (expr);
+		}
+
+		void MakeAccesors (ResolveContext rc, Expression right_side, int count)
+		{
+			accessors = new Expression [count];
+			for (int i = 0; i < count; i++)
+				accessors [i] = new MemberAccess (local, "Item" + (i+1)).Resolve (rc);
+		}
+		
+		public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
+		{
+			Type rt = right_side.Type;
+			local = new LocalTemporary (rt);
+
+			int arity = TypeManager.GetTupleArity (rt);
+			if (arity == 0){
+				ec.Report.Error (-40, Location, "Invalid source type {0} for assigning to tuple", TypeManager.CSharpName (rt));
+				return null;
+			}
+			
+			MakeAccesors (ec, right_side, arity);
+
+			for (int i = 0; i < expressions.Count; i++){
+				expressions [i] = expressions [i].ResolveLValue (ec, accessors [i]);
+				if (expressions [i] == null)
+					return null;
+				if (!(expressions [i] is IAssignMethod)){
+					Error_ValueAssignment (ec, loc);
+					return null;
+				}
+			}
+
+			type = right_side.Type;
+			eclass = ExprClass.Variable;
+			
+			return this;
+		}
+
+		protected override Expression DoResolve (ResolveContext ec)
+		{
+			return this;
+		}
+
+		public override void Emit (EmitContext ec)
+		{
+			
+		}
+
+		public void AddressOf (EmitContext ec, AddressOp mode)
+		{
+			Console.WriteLine ("AddressOf {0}", mode);
+		}
+
+		void IAssignMethod.Emit (EmitContext ec, bool leave_copy)
+		{
+			Console.WriteLine ("IAssignMethod.Emit {0}", leave_copy);
+		}
+
+		void IAssignMethod.EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
+		{
+			source.Emit (ec);
+			ILGenerator ig = ec.ig;
+
+			local.Store (ec);
+			
+			if (prepare_for_load)
+				throw new NotImplementedException ("Tuple expression has no prepare_for_load support");
+			
+			for (int i = 0; i < expressions.Count; i++){
+				IAssignMethod iam = expressions [i] as IAssignMethod;
+
+				iam.EmitAssign (ec, accessors [i], false, false);
+			}
+			
+			if (leave_copy)
+				local.Emit (ec);
+		}
+		
+		public override Expression CreateExpressionTree (ResolveContext ec)
+		{
+			return null;
+		}
+	}
 }
Index: cs-parser.jay
===================================================================
--- cs-parser.jay	(revision 148775)
+++ cs-parser.jay	(working copy)
@@ -3007,7 +3007,7 @@
 		var lt = (Tokenizer.LocatedToken) $1;
 	       $$ = new CompletionSimpleName (MemberName.MakeName (lt.Value, null), lt.Location);
 	  }
-	| parenthesized_expression
+	| parenthesized_or_tuple_expression 
 	| default_value_expression
 	| member_access
 	| invocation_expression
@@ -3049,10 +3049,13 @@
 	| OPEN_PARENS_LAMBDA
 	;
 
-parenthesized_expression
-	: OPEN_PARENS expression CLOSE_PARENS
+parenthesized_or_tuple_expression
+	: OPEN_PARENS expression_or_expression_list CLOSE_PARENS
 	  {
-		$$ = new ParenthesizedExpression ((Expression) $2);
+		if ($2 is Expression)
+			$$ = new ParenthesizedExpression ((Expression) $2);
+		else
+			$$ = $2;
 	  }
 	| OPEN_PARENS expression COMPLETE_COMPLETION
 	  {
@@ -3315,6 +3318,23 @@
 		$$ = list;
 	  }
 	;
+
+	// 
+	// Memory optimization: this is called from parenthesized_expression
+	// and most of the time it will have a single expression, so return
+	// that instead of the list of expressions.
+	//
+expression_or_expression_list 
+	: expression
+	| expression_or_expression_list COMMA expression {
+	        var el = $1 as TupleExpression;
+
+	  	if (el == null)
+			el = new TupleExpression ((Expression) $1);
+		el.Add ((Expression) $3);
+		$$ = el;
+	  }
+	;
 	
 expression_list_arguments
 	: expression_list_argument
@@ -3853,10 +3873,14 @@
 	  {
 		$$ = new Binary (Binary.Operator.Subtraction, (Expression) $1, (Expression) $3);
 	  }
-  	| parenthesized_expression MINUS multiplicative_expression
+  	| parenthesized_or_tuple_expression MINUS multiplicative_expression
 	  {
 	  	// Shift/Reduce conflict
-		$$ = new Binary (Binary.Operator.Subtraction, (Expression) $1, (Expression) $3);
+		if (!($1 is ParenthesizedExpression)){
+			Report.Error (-39, lexer.Location, "Expected a parenthesized expression, but got a tuple"); 
+			$$ = null;
+		} else
+			$$ = new Binary (Binary.Operator.Subtraction, (Expression) $1, (Expression) $3);
   	  }
 	| additive_expression AS type
 	  {
