diff --git a/docs/project/cpp_style_guide.md b/docs/project/cpp_style_guide.md index 4d48df04fb8d..94beaa8f940a 100644 --- a/docs/project/cpp_style_guide.md +++ b/docs/project/cpp_style_guide.md @@ -16,10 +16,14 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [General naming rules](#general-naming-rules) - [File names](#file-names) - [Syntax and formatting](#syntax-and-formatting) + - [Line comments](#line-comments) + - [Initialization](#initialization) + - [Passing addresses](#passing-addresses) - [Naming variable types and the use of `auto`](#naming-variable-types-and-the-use-of-auto) - [Copyable and movable types](#copyable-and-movable-types) - [Static and global variables](#static-and-global-variables) - [Foundational libraries and data types](#foundational-libraries-and-data-types) + - [Iterative algorithms](#iterative-algorithms) - [Suggested `.clang-format` contents](#suggested-clang-format-contents) @@ -98,31 +102,6 @@ these. - Only declare one variable at a time (declaring multiple variables requires confusing repetition of part of the type). - Write `const` before the type when at the outer level: `const int N = 42;`. -- Only use line comments (with `//`, not `/* ... */`), on a line by - themselves, except for - [argument name comments](https://clang.llvm.org/extra/clang-tidy/checks/bugprone/argument-comment.html), - [closing namespace comments](https://google.github.io/styleguide/cppguide.html#Namespaces), - and similar structural comments. In particular, don't append comments about - a line of code to the end of its line: - - ``` - int bad = 42; // Don't comment here. - - // Instead comment here. - int good = 42; - - // Closing namespace comments are structural, and both okay and expected. - } // namespace MyNamespace - ``` - - This dogfoods our planned commenting syntax for Carbon. It also provides a - single, consistent placement rule. It also provides more resilience against - automated refactorings. Those changes often make code longer, which forces - ever more difficult formatting decisions, and can easily spread one line - across multiple lines, leaving it impossible to know where to place the - comment. Comments on their own line preceding such code, while still - imprecise, are at least less confusing over the course of such refactorings. - - Use the `using`-based type alias syntax instead of `typedef`. - Don't use `using` to support unqualified lookup on `std` types; for example, `using std::vector;`. This also applies to other short namespaces, @@ -132,51 +111,8 @@ these. - An exception is made for functions like `std::swap` that are intentionally called using ADL. This pattern should be written as `{ using std::swap; swap(thing1, thing2); }`. -- For initialization: - - Use assignment syntax (`=`) when initializing directly with the intended - value (or with a braced initializer directly specifying that value). - - Prefer braced initialization for aggregate initialization, such as - structs, pairs, and initializer lists. - - Use designated initializers (`{.a = 1}`) when possible for structs, - but not for pairs or tuples. Prefer to only include the typename - when required to compile (`WizType{.a = 1}`). This is analogous to - how structs and tuples would be written in Carbon code. - - Avoid braced initialization for types that define a constructor, - except as an initializer list - (`llvm::SmallVector v = {0, 1};`), `std::pair`, or - `std::tuple`. Never use it with `auto` (`auto a = {0, 1}`). - - Prefer parenthesized initialization (`FooType foo(10);`) in most other - cases. - - Braced initialization without `=` (`BarType bar{10}`) should be treated - as a fallback, preferred only when other constructor syntax doesn't - compile. - - Some additional commentary is in - [Abseil's tip #88](https://abseil.io/tips/88#best-practices-for-initialization), - although these guidelines differ slightly. - Always mark constructors `explicit` unless there's a specific reason to support implicit or `{}` initialization. -- When passing an object's address as an argument, use a reference unless one - of the following cases applies: - - - If the parameter is optional, use a pointer and document that it may be - null. - - If it is captured and must outlive the call expression itself, use a - pointer and document that it must not be null (unless it is also - optional). - - - When storing an object's address as a non-owned member, prefer - storing a pointer. For example: - - ```cpp - class Bar { - public: - // `foo` must not be null. - explicit Bar(Foo* foo) : foo_(foo) {} - private: - Foo* foo_; - }; - ``` - - Always use braces for conditional, `switch`, and loop statements, even when the body is a single statement. - Within a `switch` statement, use braces after a `case` label when @@ -198,6 +134,79 @@ these. `protected`. This is motivated by the `misc-non-private-member-variables-in-classes` tidy check. +#### Line comments + +Only use line comments (with `//`, not `/* ... */`), on a line by themselves, +except for +[argument name comments](https://clang.llvm.org/extra/clang-tidy/checks/bugprone/argument-comment.html), +[closing namespace comments](https://google.github.io/styleguide/cppguide.html#Namespaces), +and similar structural comments. In particular, don't append comments about a +line of code to the end of its line: + +``` +int bad = 42; // Don't comment here. + +// Instead comment here. +int good = 42; + +// Closing namespace comments are structural, and both okay and expected. +} // namespace MyNamespace +``` + +This dogfoods our planned commenting syntax for Carbon. It also provides a +single, consistent placement rule. It also provides more resilience against +automated refactorings. Those changes often make code longer, which forces ever +more difficult formatting decisions, and can easily spread one line across +multiple lines, leaving it impossible to know where to place the comment. +Comments on their own line preceding such code, while still imprecise, are at +least less confusing over the course of such refactorings. + +#### Initialization + +Initialization syntax has guidelines based on what compiles. See also +[Abseil's tip #88](https://abseil.io/tips/88#best-practices-for-initialization), +although these guidelines differ slightly. + +- Use assignment syntax (`=`) when initializing directly with the intended + value (or with a braced initializer directly specifying that value). +- Prefer braced initialization for aggregate initialization, such as structs, + pairs, and initializer lists. + - Use designated initializers (`{.a = 1}`) when possible for structs, but + not for pairs or tuples. Prefer to only include the typename when + required to compile (`WizType{.a = 1}`). This is analogous to how + structs and tuples would be written in Carbon code. + - Avoid braced initialization for types that define a constructor, except + as an initializer list (`llvm::SmallVector v = {0, 1};`), + `std::pair`, or `std::tuple`. Never use it with `auto` + (`auto a = {0, 1}`). +- Prefer parenthesized initialization (`FooType foo(10);`) in most other + cases. +- Braced initialization without `=` (`BarType bar{10}`) should be treated as a + fallback, preferred only when other constructor syntax doesn't compile. + +#### Passing addresses + +When passing an object's address as an argument, use a reference unless one of +the following cases applies: + +- If the parameter is optional, use a pointer and document that it may be + null. +- If it is captured and must outlive the call expression itself, use a pointer + and document that it must not be null (unless it is also optional). + + - When storing an object's address as a non-owned member, prefer storing a + pointer. For example: + + ```cpp + class Bar { + public: + // `foo` must not be null. + explicit Bar(Foo* foo) : foo_(foo) {} + private: + Foo* foo_; + }; + ``` + ### Naming variable types and the use of `auto` We generally use `auto` for most local variables when a type can be inferred, @@ -235,12 +244,6 @@ class entity, we might call these `class_id`, `class_inst_id`, and patterns that tend to occur while building compilers. - They also minimize the vocabulary type friction when using actual LLVM and Clang APIs. -- In explorer, prefer standard C++ facilities, but use LLVM facilities when - there is no standard equivalent. - - This approach is aimed to make the explorer codebase more approachable - to new contributors. - - In explorer, performance is not a high priority, and friction with LLVM - and Clang APIs is much less of a concern. - Do not add other third-party library dependencies to any code that might conceivably be used as part of the compiler or runtime. - Compilers and runtime libraries have unique constraints on their @@ -248,6 +251,18 @@ class entity, we might call these `class_id`, `class_inst_id`, and layers to be under the LLVM license that the Carbon project as a whole uses (as well as LLVM itself). +### Iterative algorithms + +We choose iterative over recursive algorithms, and use clang-tidy to help +enforce this. We expect Carbon to be used in codebases which stress test the +compiler, for example due to complex interactions of APIs or simply due to +repetitive code structures from code generators. + +Other solutions that make recursion work better have been considered, but we +prefer iteration for its performance and robustness. For example, Clang launches +threads when it gets close to a stack limit; that has performance overhead and +relies on developers correctly identifying recursion which may have limits. + ## Suggested `.clang-format` contents See this repository's [`.clang-format` file](/.clang-format).