Compiler Updates - C# 3.0

by Miguel de Icaza

Progress on the C# 3.0 front.

Marek Safar has started blogging. Marek has been a contributor to the Mono C# compiler for a long time and has made the compiler very pleasant to use (fixing bugs, improving errors and warnings and doing some large changes).

Today Marek checked in his code to add C# 3.0 Extensions Methods to the compiler. More about it on his debut blog entry.

In the last couple of weeks I implemented the parsing support for C# 3.0 lambda expressions and coded the support for explicitly typed lambdas. Today I added support for overload resolution in lambda expressions.

Implementing lambda expression support in the compiler was challenging and incredibly fun.

Parsing Lambda Expressions: The first problem was that the grammar for describing the parameters in a lambda expression caused a lot of conflicts with our yacc-based parser. For example, this is a valid lambda expression:

	type var = (x, y) => x + y;

The above is problematic because a parser would not be able to tell when it finds the first open parenthesis if it was a parameter list for a lambda expression, or if it is just a parenthesized expression).

I originally feared that lambda expressions would force us to rewrite the parser from yacc into a hand-coded parser.

But I came up with a cute solution: when the tokenizer sees the first parenthesis, it starts a minimal hand-coded top-down parser that will parse the lambda parameter list plus the optional arrow, and depending on the result return either a OPEN_PARENS or a OPEN_LAMBDA_PARENS token.

Implicit Typed Parameters: The second challenge was implicitly typed parameters. Unlike anonymous methods which require the type of the parameters to be specified, lambda expressions do not require them, so it is possible to write code like this:

	delegate string rets (string s);
	delegate int reti (int i);

	Foo (rets s)
	{
		Console.WriteLine (s ("hello, "));
	}

	Foot (reti i)
	{
		Console.WriteLine (i (10));
	}
	
	Foo (x => x + "world");

The complication is that the lambda expression 'x + "world"' needs to be probed for validity against `reti' and `rets', this means that x can be either a string or an int.

For this to work, I added a cloning infrastructure that would clone blocks and expressions and attempt to resolve each expression without side effects until a valid lambda expression is parsed.

Return Rules: Finally, the last interesting bit was the implementation that allowed code blocks or expressions to return values. The body of a lambda expression can be either a block or an expression.

The problem with expressions is that depending on the delegate type, we might or might not be interested in the result of the expression (void return, no interest; typed return, interest).

I believe I have a quite elegant solution in the form of the ContextualReturn statement. This is a Statement that is pretty much a copy of our "Return" statement implementation. The only difference is that it would become a "return-with-value" if there is an expected return type and would become a "expression-statement; return" in the case that this was a void return.

Posted on 15 Feb 2007