From 0ea8fb2e74cf11c2ebf99039f4e419442f22a5ec Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 27 Aug 2026 18:51:30 +0000 Subject: [PATCH] Add documentation for our constrained overload set idiom. (#7681) As used in #7679. --- toolchain/docs/idioms.md | 58 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/toolchain/docs/idioms.md b/toolchain/docs/idioms.md index b50428c1c86c..0effe73fef0b 100644 --- a/toolchain/docs/idioms.md +++ b/toolchain/docs/idioms.md @@ -15,6 +15,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Abbreviations used in the code (AKA Carbon abbreviation decoder ring)](#abbreviations-used-in-the-code-aka-carbon-abbreviation-decoder-ring) - [`.def` files](#def-files) - [EnumBase types](#enumbase-types) + - [Overloads and conditional overlods](#overloads-and-conditional-overlods) - [Index types](#index-types) - [ValueStore](#valuestore) - [Template metaprogramming](#template-metaprogramming) @@ -112,6 +113,63 @@ In code, these types and values can be used directly in a `switch`. They will convert to an internal _actual_ `enum class` for the `switch`, and receive corresponding compiler safety checks that all enum values are handled. +### Overloads and conditional overlods + +Sometimes we want to declare a set of overloaded functions elements of a set of +types declared in a `.def` file, with the definitions of those functions spread +out across multiple files. For example, this is common with the `Handle*` +functions used in various layers of the toolchain: + +```c++ +namespace Carbon::Parse { + +// Declare handlers for each parse state. +#define CARBON_PARSE_STATE(Name) auto Handle##Name(Context& context) -> void; +#include "toolchain/parse/state.def" + +} // namespace Carbon::Parse +``` + +This avoids the need to manually declare each such function, and provides nice +errors if such a function is defined with the wrong signature: Clang diagnoses +that a non-`static` function lacks a forward declaration, or that a `static` +function has no callers. + +In some cases, we don't want an overload for _each_ element of a `.def` file, +but only for some subset of those elements, or we want different signatures for +different elements. We accomplish this by defining a template that maps the +relevant element to the corresponding function type, and maps elements that +should be omitted to a placeholder signature (typically `auto() -> void`): + +```c++ +namespace Internal { +// Computes the function type to use for DoThings for InstT. +template +using FunctionTypeForDoThings = std::conditional_t< + HasDoThings, + auto(Context& context, InstT inst) -> void, + auto()->void>; +} // namespace Internal +``` + +We then delete the placeholder signature, and use the `.def` file to generate +one declaration per element: + +```c++ +auto DoThings() -> void = delete; + +// Do a thing. For each instruction kind that does things, an overload should be +// defined with the signature: +// +// auto DoThings(Context& context, InstT inst) -> void; +#define CARBON_SEM_IR_INST_KIND(InstT) \ + Internal::FunctionTypeForDoThings DoThings; +#include "toolchain/sem_ir/inst_kind.def" +``` + +This results in all the "omitted" signatures being redeclarations of the deleted +signature. + ## Index types Carbon makes frequent use of