Two afterthoughts:

- IINM, the different syntax for blocks between option #2 and option #3 is not 
that drastic, so if we choose one and decide we prefer the other, it might not 
be too hard to change.

- In option #4, we can't completely *turn off* typechecking -- that's unsound. 
(For example, inside the unchecked part you could assign the wrong type to a 
variable or data structure.) But we could avoid certain checks (like comparing 
the result type of the two arms of an if). Not that I'm advocating option #4. :)

Dave

On Nov 23, 2010, at 2:53 PM, David Herman wrote:

> Of these, I like option #3 the most.
> 
> I should say, I think anywhere that we have statements in the grammar, we 
> could actually allow them to be expressions of type (), and ISTM that would 
> be equally workable for option #2 or option #3. I'd be open to that 
> alternative, since in *surface* syntax you still have the look and feel of C, 
> but you get higher refactoring flexibility.
> 
> Dave
> 
> On Nov 23, 2010, at 2:34 PM, Graydon Hoare wrote:
> 
>> Hi,
>> 
>> Some of you may have noticed that in the rewrite from rustboot to rustc 
>> we're becoming substantially more expression-language-ish. This is mostly a 
>> result of me yielding to the preferences of other developers (and LLVM's 
>> semantics), as well as some hint that things get much easier in syntax 
>> extensions and calculating compile-time-constants if we permit more 
>> "statement-ish" forms as expressions. Particularly conditionals.
>> 
>> We've run into a (common, seen in many other languages) sort of problem 
>> along the way here, which is that some expressions are implicitly ignored 
>> (or must be, due to being in an ignored context) whereas others are not. We 
>> have a nil-type (), but we don't always have sensible rules for forcing 
>> things to have the nil type by context.
>> 
>> This email is a poll of alternative solutions. I'll give two example cases 
>> and ask people for their input on which modification of the rules feels best.
>> 
>> Example case that does compile:
>> 
>> A:  auto x = if (foo()) { 10; } else { 11; };
>> 
>> Example case that does not compile:
>> 
>> B:  if (foo()) { 10; } else { "hello"; }
>> 
>> We can write this in rust at the moment, but in the rustc typechecking rules 
>> it will fail to compile, because 'if' is an expression-statement, 
>> expressions have types, and the types of the two branches (judged as the 
>> last statement's expression value, if it's an expression, or else nil) are 
>> of different types.
>> 
>> Here are some approaches to solving this example. Please pick the one you 
>> like the most:
>> 
>> (1) Kick all branchy expressions out of the expression grammar, put them 
>> back in the statement grammar. Case B will compile, and case A must be 
>> rewritten like so:
>> 
>> A:  auto x = { auto t = 11; if (foo()) { t = 10; }; t; };
>> 
>> This is the C-with-GNU-extensions model.
>> 
>> (2) Hoist all statements up into the expression language and make semicolon 
>> into a sequencing operator, with a trailing-semi ignored by the parser. Then 
>> we need to rewrite only the second case to force unit types in the 
>> to-be-ignored differing branches.
>> 
>> B:  if (foo()) { 10; () } else { "hello"; () }
>> 
>> Though we'd also be *allowed* to rewrite the first case to drop the 
>> semicolons:
>> 
>> A:  auto x = if (foo() { 10 } else { 11 };
>> 
>> This is the Ocaml approach.
>> 
>> (3) A slightly weaker form of (2), which is to reformulate blocks with the 
>> following grammar:
>> 
>>   block ::=  { [ stmt ; ]* expr? }
>> 
>> In other words, every block becomes a brace-enclosed sequence of 
>> semicolon-terminated statements, followed by an optional expr. If the expr 
>> is missing, it is implied as (). In this case we'd be rewriting only the 
>> first case:
>> 
>> A:  auto x = if (foo()) { 10 } else { 11 };
>> 
>> This is similar to the Ocaml rule in practice, except that it makes the 
>> presence or absence of the final semicolon in a block equivalent to ending 
>> the block with the nil type. This is a possible hazard (especially during 
>> refactoring or editing) to users who want to write a value-producing block 
>> but accidentally semicolon-terminate the last expression; but it's not a 
>> huge hazard since the typechecker will tell them the value they produced is 
>> of nil type. It just might be hit a lot.
>> 
>> (4) Statically determine the contexts in which an expression's value "will 
>> be used" in an outer expression, and only typecheck those contexts. This 
>> permits both of the examples to compile as-is, but it's the most unorthodox 
>> approach, and poses a refactoring hazard as code may become type-invalid 
>> when nested into an expression context that "uses" its previously-ignored 
>> result. Again, as in (3) the typechecker will catch these cases, but they 
>> might happen more or less often than those in (3).
>> 
>> We can't think of any other options. Significant whitespace is not an option 
>> :)
>> 
>> Personally my knee-jerk reaction is to embrace (1) since I like statements 
>> anyway, but I can see plausible arguments for the other 3. Can I get a show 
>> of hands? We have to pick something.
>> 
>> -Graydon
>> _______________________________________________
>> Rust-dev mailing list
>> [email protected]
>> https://mail.mozilla.org/listinfo/rust-dev
> 
> _______________________________________________
> Rust-dev mailing list
> [email protected]
> https://mail.mozilla.org/listinfo/rust-dev

_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to