"What useful expressiveness will be impossible in a language where an expression is not a statement?"
None.
The definitions of "expression" and "statement" are largely arbitrary. Different languages define them differently. I know of no programming language whose way of defining these terms makes any particular construct impossible to express. In some languages, some constructs may be a bit more awkward; in others, it may be easier to write code that's difficult to read because a single statement depends on multiple side effects.
Almost all programming languages are Turing-complete and are able to express the same behaviors as any other Turing-complete languages.
Two good examples are Pascal and C.
In Pascal, an assignment is a statement, not an expression. A subroutine can be either a procedure (which doesn't return a value) or a function (which does). A procedure call is a statement, and cannot be part of an expression. A function call is an expression, and cannot appear in a statement unless you explicitly do something with the result, such as assigning it to a variable. Statements can contain expressions, but expressions cannot contain statements.
In C, an assignment is an expression. All subroutines are functions; functions that return no value are declared with a return type of void
. A function call is an expression; it can be made into a statement by adding a semicolon. The result of a function call, or of any expression, can be silently discarded. Any expression can be turned into a statement by adding a semicolon. As in Pascal, statements can contain expressions, but expressions cannot contain statements (but gcc provides statement expressions as a language extension).
Here's a typical C construct that depends on the ability to treat assignments as expressions (item
is some arbitrary type, and last_item
is a constant of that type):
item x;
while ((x = get_next_item()) != last_item) {
do_something_with(x);
}
In Pascal, you can implement exactly the same behavior like this (if I remember the syntax correctly):
var
x: item;
done: boolean = false;
begin
while not done do
begin
x := Next_Item;
if x = last_item then
done := true
else
do_something_with(x);
end;
end;