Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 204 additions & 0 deletions proposals/p5671.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
# Do...while loops

<!--
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/####)

<!-- toc -->

## Table of contents

- [Abstract](#abstract)
- [Problem](#problem)
- [Background](#background)
- [Proposal](#proposal)
- [Details](#details)
- [Syntax](#syntax)
- [Semantics](#semantics)
- [Examples](#examples)
- [Interaction with break and continue](#interaction-with-break-and-continue)
- [Rationale](#rationale)
- [Alternatives considered](#alternatives-considered)
- [Alternative syntax forms](#alternative-syntax-forms)
- [Not adding do...while loops](#not-adding-dowhile-loops)
- [Using repeat...until instead](#using-repeatuntil-instead)

<!-- tocstop -->

## Abstract

This proposal adds `do...while` loops to Carbon, providing a post-test loop construct that executes the loop body at least once before checking the condition. This complements the existing `while` and `for` loop constructs.

## Problem

Carbon currently provides `while` and `for` loops, but lacks a post-test loop construct. There are common programming patterns where you need to execute a block of code at least once and then repeat it based on a condition. Currently, these patterns require either:

1. Code duplication (writing the loop body before the `while` loop)
2. Using a `while (true)` loop with a `break` statement
3. Using a boolean flag to ensure the first iteration

These workarounds make code less readable and more error-prone. A `do...while` construct would provide a clean, idiomatic solution for these use cases.

## Background

Many programming languages provide post-test loop constructs:

- C, C++, Java, C#, and JavaScript use `do...while`
- Some languages like Pascal use `repeat...until` (with inverted condition logic)
- Python notably lacks this construct, leading to similar workarounds

Common use cases for post-test loops include:

- Input validation loops (prompt user until valid input is provided)
- Menu systems (show menu at least once, repeat based on user choice)
- Game loops (execute game logic at least once per frame)
- Retry mechanisms (attempt operation at least once, retry on failure)
- Processing loops where you need to read/process at least one item

## Proposal

Add `do...while` loops to Carbon with the following syntax:

```carbon
do {
// statements
} while (condition);
```

The loop body executes at least once, and then the condition is evaluated. If the condition is `True`, the loop continues; if `False`, the loop terminates.

## Details

### Syntax

The syntax for `do...while` loops is:

> `do {` _statements_ `} while (` _boolean expression_ `);`

Key syntax elements:
- The `do` keyword introduces the loop
- The loop body is enclosed in braces `{}`
- The `while` keyword and condition follow the closing brace
- A semicolon `;` terminates the statement

### Semantics

1. The loop body executes unconditionally on the first iteration
2. After each iteration, the boolean expression is evaluated
3. If the expression evaluates to `True`, execution returns to the beginning of the loop body
4. If the expression evaluates to `False`, execution continues after the loop
5. The boolean expression must have type `Bool` or be implicitly convertible to `Bool`

### Examples

**Input validation:**
```carbon
var input: String;
do {
Print("Enter a positive number: ");
input = ReadLine();
} while (!IsValidPositiveNumber(input));
```

**Menu system:**
```carbon
var choice: Int;
do {
PrintMenu();
choice = ReadChoice();
ProcessChoice(choice);
} while (choice != 0);
```

**Processing with at-least-once guarantee:**
```carbon
var data: Array(String) = LoadInitialData();
do {
ProcessBatch(data);
data = LoadNextBatch();
} while (!data.IsEmpty());
```

**Retry mechanism:**
```carbon
var success: Bool;
var attempts: Int = 0;
do {
success = TryOperation();
++attempts;
} while (!success && attempts < MaxRetries);
```

### Interaction with break and continue

`do...while` loops support the same control flow statements as other loops:

- `break` immediately exits the loop, skipping the condition check
- `continue` skips to the condition check, potentially starting the next iteration

**Example with break:**
```carbon
do {
var input: String = ReadLine();
if (input == "quit") {
break;
}
ProcessInput(input);
} while (true);
```

**Example with continue:**
```carbon
do {
var value: Int = GetNextValue();
if (value < 0) {
continue; // Skip negative values
}
ProcessValue(value);
} while (HasMoreValues());
```

## Rationale

This proposal advances Carbon's goals in several ways:

- **[Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)**: `do...while` loops make post-test loop patterns more explicit and readable than workarounds with `while (true)` and `break` statements.

- **[Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code)**: C++ developers are familiar with `do...while` loops, making Carbon more approachable for migration.

- **[Performance-critical software](/docs/project/goals.md#performance-critical-software)**: Eliminates the need for boolean flags or code duplication that might impact performance in tight loops.

The syntax follows Carbon's existing patterns:
- Consistent with `while` loops in using parentheses around the condition
- Consistent with Carbon's requirement for braces around statement blocks
- The semicolon terminator follows C-family language conventions

## Alternatives considered

### Not adding do...while loops

We could choose not to add `do...while` loops and continue using workarounds. However:

- Code duplication is error-prone and violates DRY principles
- `while (true)` with `break` is less clear about intent
- Boolean flag approaches add unnecessary complexity
- The construct is common enough in other languages to warrant inclusion

### Using repeat...until instead

**Alternative syntax:**
```carbon
repeat {
// statements
} until (condition);
```

This was considered but rejected because:
- It inverts the condition logic compared to `while` loops, which could be confusing
- `do...while` is more familiar to developers from C-family languages
- It would require introducing two new keywords (`repeat` and `until`) instead of reusing `while`
- The condition inversion makes it harder to convert between `while` and `do...while` loops
Loading