From cbd79529b2480f2289544ccf1ccf40438edf8454 Mon Sep 17 00:00:00 2001 From: josh11b <15258583+josh11b@users.noreply.github.com> Date: Thu, 18 Jun 2026 13:55:07 -0700 Subject: [PATCH] Update design docs to reflect proposal #1885: `for` statement and user types (#7350) Assisted-by: Gemini via Antigravity --------- Co-authored-by: Josh L --- docs/design/README.md | 4 +- docs/design/control_flow/loops.md | 59 +++++++++++++++++++++--- docs/design/expressions/member_access.md | 2 +- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/docs/design/README.md b/docs/design/README.md index 91d36f831ce0..f3e43873e02c 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -1450,7 +1450,7 @@ Core.Print("Done!"); example, this prints each `String` value in `names`: ```carbon -for (var name: String in names) { +for (name: String in names) { Core.Print(name); } ``` @@ -1471,7 +1471,7 @@ steps until a manual step is hit (if no manual step is hit, all steps are processed): ```carbon -for (var step: Step in steps) { +for (step: Step in steps) { if (step.IsManual()) { Core.Print("Reached manual step!"); break; diff --git a/docs/design/control_flow/loops.md b/docs/design/control_flow/loops.md index bdd5639efef1..c17f5695d0cf 100644 --- a/docs/design/control_flow/loops.md +++ b/docs/design/control_flow/loops.md @@ -14,6 +14,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Details](#details) - [`while`](#while) - [`for`](#for) + - [Ranged-for for user-defined types](#ranged-for-for-user-defined-types) - [`break`](#break) - [`continue`](#continue) - [Alternatives considered](#alternatives-considered) @@ -51,20 +52,59 @@ Print("Done!"); `for` statements support range-based looping, typically over containers. Syntax is: -> `for (` _var declaration_ `in` _expression_ `) {` _statements_ `}` +> `for (` _pattern_ `in` _expression_ `) {` _statements_ `}` + +For consistency with function parameters and other pattern matching contexts, +the pattern defaults to value (immutable) bindings, like `let`. For example, this prints all names in `names`: ```carbon -for (var name: String in names) { +for (name: strbuf in names) { Print(name); } ``` -`PrintNames()` prints each `String` in the `names` `List` in iteration order. +This default can be overridden by adding the `var` keyword: -TODO: Add semantics discussion from -[#1885: ranged-based `for` for user-defined types](https://github.com/carbon-language/carbon-lang/pull/1885). +```carbon +for (var name: strbuf in names) { + // `name` can be modified, but this will not modify the underlying `names` container. +} +``` + +Temporary entities on the right-hand side of `in` remain alive during the +execution of the `for` loop to prevent invalid memory access. + +#### Ranged-for for user-defined types + +User types can enable support for ranged-for loops by implementing the `Iterate` +interface: + +```carbon +interface Iterate { + let ElementType:! type; + let CursorType:! type; + fn NewCursor(self) -> CursorType; + fn Next(self, ref cursor: CursorType) -> Optional(ElementType); +} +``` + +The cursor tracks progression, and the `Next` method advances the cursor and +returns an `Optional` value. An empty `Optional` indicates that we have reached +the end. + +A `for` loop on a container of a type that implements `Iterate` behaves +conceptually as (though an API for `Optional` has not been approved): + +```carbon +var cursor: range.(Iterate.CursorType) = range.(Iterate.NewCursor)(); +var iter: Optional(range.(Iterate.ElementType)) = range.(Iterate.Next)(&cursor); +while (iter.HasValue()) { + ExecuteForBlock(iter.Get()); + iter = container.(Iterate.Next)(ref cursor); +} +``` ### `break` @@ -77,7 +117,7 @@ For example, this processes steps until a manual step is hit (if no manual step is hit, all steps are processed): ```carbon -for (var step: Step in steps) { +for (step: Step in steps) { if (step.IsManual()) { Print("Reached manual step!"); break; @@ -99,7 +139,7 @@ empty lines: ```carbon var f: File = OpenFile(path); while (!f.EOF()) { - var line: String = f.ReadLine(); + var line: strbuf = f.ReadLine(); if (line.IsEmpty()) { continue; } @@ -115,6 +155,9 @@ while (!f.EOF()) { - [Include semisemi `for` loops](/proposals/p000353-for-loops.md#include-semisemi-for-loops) - [Multi-variable bindings](/proposals/p000353-for-loops.md#multi-variable-bindings) - [`:` versus `in`](/proposals/p000618-var-ordering.md#-versus-in) + - [Atomic methods for `Iterate`](/proposals/p001885-for-statement-and-user-types.md#atomic-methods-for-iterate) + - [Using an iterator instead of a cursor](/proposals/p001885-for-statement-and-user-types.md#using-an-iterator-instead-of-a-cursor) + - [Support getter for both `T` and `T*` with `Iterate`](/proposals/p001885-for-statement-and-user-types.md#support-getter-for-both-t-and-t-with-iterate) - [Optional braces](/proposals/p000623-require-braces.md#optional-braces) - [Optional parentheses](/proposals/p000623-require-braces.md#optional-parentheses) @@ -128,3 +171,5 @@ while (!f.EOF()) { [#618: `var` ordering](https://github.com/carbon-language/carbon-lang/pull/618) - Proposal [#623: Require braces](https://github.com/carbon-language/carbon-lang/pull/623) +- Proposal + [#1885: `for` statement and user types](https://github.com/carbon-language/carbon-lang/pull/1885) diff --git a/docs/design/expressions/member_access.md b/docs/design/expressions/member_access.md index 072f443d0763..04942b406f39 100644 --- a/docs/design/expressions/member_access.md +++ b/docs/design/expressions/member_access.md @@ -740,7 +740,7 @@ base class WidgetBase { alias Draw = Renderable.Draw; fn DrawAll[T:! Renderable](v: Vector(T)) { - for (var w: T in v) { + for (w: T in v) { // ✅ OK. Unqualified lookup for `Draw` finds alias `WidgetBase.Draw` // to `Renderable.Draw`, which does not perform `impl` lookup yet. // Then the compound member access expression performs `impl` lookup