mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 18:21:16 +01:00
Require braces (#623)
Require braces, never optional, particularly in control flow like `if`/`else`. Co-authored-by: Richard Smith <richard@metafoo.co.uk> Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Richard Smith
Chandler Carruth
parent
034f3600e3
commit
cc56afb79e
@@ -61,5 +61,6 @@ request:
|
||||
- [0555 - Operator precedence](p0555.md)
|
||||
- [0601 - Operator tokens](p0601.md)
|
||||
- [0618 - var ordering](p0618.md)
|
||||
- [0623 - Require braces](p0623.md)
|
||||
|
||||
<!-- endproposals -->
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
# Require braces
|
||||
|
||||
<!--
|
||||
Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
Exceptions. See /LICENSE for license information.
|
||||
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
-->
|
||||
|
||||
[Pull request](https://github.com/carbon-language/carbon-lang/pull/623)
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Problem](#problem)
|
||||
- [Background](#background)
|
||||
- [Consistency](#consistency)
|
||||
- [C++ style rules](#c-style-rules)
|
||||
- [`goto fail`](#goto-fail)
|
||||
- [Proposal](#proposal)
|
||||
- [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals)
|
||||
- [Alternatives considered](#alternatives-considered)
|
||||
- [Optional braces](#optional-braces)
|
||||
- [Optional parentheses](#optional-parentheses)
|
||||
- [`elif`](#elif)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
## Problem
|
||||
|
||||
In C++, braces are often optional, such as in:
|
||||
|
||||
```
|
||||
for (Shape x : shapes)
|
||||
Draw(x);
|
||||
```
|
||||
|
||||
Carbon adopted this design choice by default in proposals
|
||||
[#285](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0285.md),
|
||||
[#340](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0340.md),
|
||||
and
|
||||
[#353](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0353.md).
|
||||
But should we keep it?
|
||||
|
||||
## Background
|
||||
|
||||
### Consistency
|
||||
|
||||
We were adopting the C++ syntax primarily for easy consistency with C++. For
|
||||
other languages:
|
||||
|
||||
- Some modern languages require braces, including Go, Rust, and Swift. Note
|
||||
some which require braces make parentheses optional.
|
||||
- Kotlin is an example modern language which does not require braces.
|
||||
- Older languages tend to make braces optional, including Java, JavaScript,
|
||||
and C#.
|
||||
|
||||
### C++ style rules
|
||||
|
||||
There is a
|
||||
[CERT rule](https://wiki.sei.cmu.edu/confluence/display/c/EXP19-C.+Use+braces+for+the+body+of+an+if%2C+for%2C+or+while+statement)
|
||||
for when to use braces.
|
||||
|
||||
### `goto fail`
|
||||
|
||||
Apple had an infamous `goto fail` bug:
|
||||
|
||||
- https://www.imperialviolet.org/2014/02/22/applebug.html
|
||||
- https://dwheeler.com/essays/apple-goto-fail.html
|
||||
|
||||
## Proposal
|
||||
|
||||
Carbon should require braces when they might otherwise be optional. We should
|
||||
continue to require parentheses. This currently comes up in `if`/`else`,
|
||||
`while`, and `for`.
|
||||
|
||||
We will allow `else if` as a special structure for repeated `if` statements due
|
||||
to their frequency. For example, `if (...) { ... } else if (...) { ... }`
|
||||
behaves identically to `if (...) { ... } else { if (...) { ... } }`.
|
||||
|
||||
## Rationale based on Carbon's goals
|
||||
|
||||
- **Software and language evolution**: Reducing parsing ambiguity of curly
|
||||
braces is important for expanding the syntactic options for Carbon.
|
||||
|
||||
- **Code that is easy to read, understand, and write**: We have a preference
|
||||
to give only one way to do things, and optional braces are inconsistent with
|
||||
that. It's also easier to understand and parse code that uses braces, and
|
||||
defends against the possibility of `goto fail`-style bugs, whether
|
||||
accidental or malicious.
|
||||
|
||||
- **Interoperability with and migration from existing C++ code**: While C++
|
||||
does make braces optional in related situations, we believe this isn't
|
||||
fundamental to migration or familiarity for C++ developers, so this goal is
|
||||
not meaningfully impacted by this change.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
### Optional braces
|
||||
|
||||
We could instead keep braces optional, such as:
|
||||
|
||||
```carbon
|
||||
if (x)
|
||||
return y;
|
||||
```
|
||||
|
||||
Advantages:
|
||||
|
||||
- Consistent with C++.
|
||||
- Allows for greater brevity in code, such as `if (!success) return error;`.
|
||||
|
||||
Disadvantages:
|
||||
|
||||
- Gives two ways of doing the same thing.
|
||||
- Style guides will make choices, creating more contextual coding style.
|
||||
- Some community members opined that requiring braces was the only
|
||||
solution which is both appealing and easy to automate formatting for.
|
||||
- A contrary opinion is that single-line if statements without braces
|
||||
are more appealing, and not significantly more difficult to
|
||||
automate.
|
||||
- More complex and harder to parse.
|
||||
|
||||
- Nested `if` statements can have unclear `else` bindings. For example:
|
||||
|
||||
```carbon
|
||||
if (x)
|
||||
if (y)
|
||||
DoIfY();
|
||||
else
|
||||
DoIfNotY();
|
||||
else
|
||||
DoIfNotX();
|
||||
```
|
||||
|
||||
- If Carbon were to reuse `if` and `else` keywords for a ternary operator,
|
||||
that could omit braces in order to avoid ambiguity. For example,
|
||||
`int x = if y then 3 else 7;`.
|
||||
|
||||
- Developers are known to make mistakes adding statements to conditionals
|
||||
missing braces, keeping consistent indentation, and missing the incorrect
|
||||
behavior due to cognitive load. For example:
|
||||
|
||||
```carbon
|
||||
if (x)
|
||||
return y;
|
||||
```
|
||||
|
||||
->
|
||||
|
||||
```carbon
|
||||
if (x)
|
||||
print("Returning y");
|
||||
return y;
|
||||
```
|
||||
|
||||
- Developers commonly want to add debugging statements to conditionals,
|
||||
and missing braces can lead to these kinds of errors.
|
||||
- It's possible that, over time, adding braces to add debug statements
|
||||
then removing the debug statements without removing statements will lead
|
||||
to braces remaining when braces are optional.
|
||||
- This can be addressed by style guides and automated formatting that
|
||||
inserts or removes braces as appropriate, cleaning up after
|
||||
temporary edits.
|
||||
- Some people feel the churn of adding braces and later removing them
|
||||
is a significant toil that can be avoided by always adding them,
|
||||
such as [here](https://wiki.c2.com/?AlwaysUseBracesOnIfThen).
|
||||
- It can more easily lead to bugs like `goto fail;`.
|
||||
- However, as the blog post points out, one can have incorrect
|
||||
indentation with braces too.
|
||||
|
||||
### Optional parentheses
|
||||
|
||||
Languages which make braces required can make parentheses optional, or even
|
||||
disallow them, such as:
|
||||
|
||||
```carbon
|
||||
if x {
|
||||
return y;
|
||||
}
|
||||
```
|
||||
|
||||
Advantages:
|
||||
|
||||
- Exchanges verbosity in syntax, requiring `{}` but removing `()`.
|
||||
- Particularly useful in that many conditionals have `{}` regardless of
|
||||
optionality, but all conditionals could in theory remove `()`.
|
||||
- Cross-language consistency with languages that require `{}`.
|
||||
|
||||
Disadvantages:
|
||||
|
||||
- Visual inconsistency with C++.
|
||||
- Parentheses make parsing less likely to encounter ambiguity.
|
||||
- Optional parentheses lead to two ways of doing the same thing, although
|
||||
disallowing them entirely could address this.
|
||||
|
||||
### `elif`
|
||||
|
||||
We could make `else if` a single token, such as `elseif` or `elif`.
|
||||
|
||||
Advantages:
|
||||
|
||||
- Can parse as a single token.
|
||||
|
||||
Disadvantages:
|
||||
|
||||
- Visual inconsistency with C++.
|
||||
- `else if` also appears in some languages that require braces, such as
|
||||
Go, Rust, and Swift.
|
||||
Reference in New Issue
Block a user