Add skills for diagnostics and builtin functions. (#7277)

Also make some updates and improvements to fine-tune existing skills.

Simplify AGENTS.md to remove redundant instructions that duplicate
information that's already in skill files -- skills should be loaded
automatically and should not need to be redundantly specified in
AGENTS.md.

Assisted-by: Gemini via Antigravity
This commit is contained in:
Richard Smith
2026-05-28 23:58:37 +00:00
committed by GitHub
parent 1028538d05
commit 1cb0eecc1e
6 changed files with 633 additions and 28 deletions
+253
View File
@@ -0,0 +1,253 @@
---
name: Builtin functions
description:
Instructions for registering, mapping, constant evaluating, and lowering
builtin functions in the Carbon toolchain.
---
# Builtin Functions in the Carbon Toolchain
<!--
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
-->
Builtin functions are compiler-recognized primitives mapping directly from
Carbon code expressions (via standard prelude bindings) to optimized backend
execution. This document defines the complete structural workflow, C++ patterns,
constant evaluation logic, machine lowering mechanics, library bindings, and
validation strategies required to implement builtin functions in the Carbon
compiler.
---
## Technical Flow & Lifecycle
```mermaid
graph TD
Src[Carbon Source Code] -->|Prelude Map| Sem[Semantic Analysis / SemIR]
Sem -->|Signature Constraint| Sig[builtin_function_kind.cpp]
Sem -->|Phase Evaluation| Eval[eval.cpp Constant Interpreter]
Sem -->|Machine Codegen| Lower[handle_call.cpp LLVM Lowering]
Eval -->|Diagnostics| Diag[diagnostics/kind.def]
Lower -->|Native Instructions| LLVM[LLVM IR Generation]
```
Adding a builtin function involves a 5-step integration:
1. **Define the Builtin Kind**: Register the enum in
[builtin_function_kind.def](../../../toolchain/sem_ir/builtin_function_kind.def).
2. **Signature & Compile-Time Registry**: Declare the mapping name, parameter
constraints, and compile-time evaluation residency in
[builtin_function_kind.cpp](../../../toolchain/sem_ir/builtin_function_kind.cpp).
3. **Compile-Time Interpreter Support**: Wire constant evaluation hooks and
bounds/exception diagnostics in
[eval.cpp](../../../toolchain/check/eval.cpp).
4. **LLVM IR Lowering Support**: Connect target machine generation in
[handle_call.cpp](../../../toolchain/lower/handle_call.cpp).
5. **Prelude Library Mapping**: Bind primitive interfaces to named builtins
under [core/prelude/](../../../core/prelude/).
---
## Detailed Step-by-Step Implementation Guide
### Step 1: Kind Definition & Registration
Register your builtin function name using the X-macro in
[builtin_function_kind.def](../../../toolchain/sem_ir/builtin_function_kind.def):
```cpp
// toolchain/sem_ir/builtin_function_kind.def
// Converts an integer type to a floating-point type.
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntConvertFloat)
```
### Step 2: Signature Validation & Compile-Time Residence
Inside
[builtin_function_kind.cpp](../../../toolchain/sem_ir/builtin_function_kind.cpp):
1. **Define Parameter Constraints**: If the parameter requires novel constraints
(e.g. "must be a float type"), define a template constraint struct checking
the matching `SemIR` type instruction (such as `FloatType` or
`FloatLiteralType`). Use pre-established semantic helpers:
- `TypeParam<I, T>`: Ensures different parameters resolve to identical type
structures (e.g., generic constraint matching).
- `AnyInt`, `AnyFloat`, `AnySizedInt`, `AnySizedFloat`, `CharCompatible`,
`StdInitializerList`, `NoReturn`.
2. **Map Literal Name & Register Constraint Signature**: Declare a `BuiltinInfo`
constant inside `namespace BuiltinFunctionInfo` matching the macro-defined
name:
```cpp
// toolchain/sem_ir/builtin_function_kind.cpp
constexpr BuiltinInfo IntConvertFloat = {
"int.convert_float", ValidateSignature<auto(AnyInt)->AnyFloat>};
```
3. **Establish Compile-Time Residency Status**: Update
`BuiltinFunctionKind::IsCompTimeOnly` to determine if a call requires
compile-time evaluation:
- **Checked/Diagnostics Primitives**: Return `true` immediately. Runtime
lowering of these is illegal (e.g. `IntConvertFloatChecked`).
- **Runtime Primitives**: Return
`AnyLiteralTypes(sem_ir, arg_ids, return_type_id)` to enforce that
expressions involving unsized literal values (like `IntLiteral()` or
`FloatLiteral()`) are evaluated exclusively at compile-time (as they lack
runtime representation).
---
### Step 3: Constant Evaluation Support
Wire the interpreter inside [eval.cpp](../../../toolchain/check/eval.cpp) to
execute compile-time computations:
1. **Implement Constant Evaluation Logic**:
- Handle the builtin case inside `MakeConstantForBuiltinCall` (which
processes the compile-time execution of the call).
- Confirm type validation phase is `Phase::Concrete` to reject incomplete
bindings:
```cpp
case SemIR::BuiltinFunctionKind::IntConvertFloat: {
if (phase != Phase::Concrete) {
return MakeConstantResult(context, call, phase);
}
return PerformIntToFloatConvert(context, loc_id, arg_ids[0], call.type_id,
/*require_exact=*/false);
}
```
- Extract inputs safely from local value stores (e.g.
`context.ints().Get(arg.int_id)` or `context.floats().Get(arg.float_id)`).
- Leverage high-precision LLVM mathematical structures (`llvm::APInt`,
`llvm::APFloat`, `llvm::APSInt`) to handle custom bits and signedness
safely.
2. **Diagnose Invalid Parameters or Exceptions**:
- Define compile-time diagnostics inside
[kind.def](../../../toolchain/diagnostics/kind.def):
```cpp
// toolchain/diagnostics/kind.def
CARBON_DIAGNOSTIC_KIND(IntTooLargeForFloatType)
```
- Emplace localized diagnostic formatting messages where they are caught in
`eval.cpp`:
```cpp
CARBON_DIAGNOSTIC(IntTooLargeForFloatType, Error,
"integer value {0} too large for floating-point type {1}",
llvm::APSInt, SemIR::TypeId);
context.emitter().Emit(loc_id, IntTooLargeForFloatType, val, dest_type_id);
```
- Return `SemIR::ErrorInst::ConstantId` to gracefully abort invalid constant
generation rather than crashing the compiler.
3. **Fast-Path Range Limits**:
- Before evaluating expensive math operations on giant exponents (e.g.
`1.0e1000000`), executing range limits check against `dest_width + 64`
(sized) or `IntStore::MaxIntWidth` (unsized) is mandatory to prevent
out-of-bounds calculations and compile-time memory exhaustion.
---
### Step 4: Machine Code Generation (LLVM Lowering)
Inside [handle_call.cpp](../../../toolchain/lower/handle_call.cpp):
1. **Map to Native LLVM Instructions**: For runtime-eligible builtins, map the
call inside `HandleBuiltinCall` to native LLVM IR builder methods:
```cpp
case SemIR::BuiltinFunctionKind::IntConvertFloat: {
auto* operand = context.GetValue(arg_ids[0]);
auto* dest_type = context.GetTypeOfInst(inst_id);
bool is_signed = IsSignedInt(context, arg_ids[0]);
context.SetLocal(
inst_id, is_signed
? context.builder().CreateSIToFP(operand, dest_type)
: context.builder().CreateUIToFP(operand, dest_type));
return;
}
```
2. **Assert on Compile-Time-Only Builtins**: Throw a hard assertion on
lowering-cases for checked validator builtins that should never hit code
generation:
```cpp
case SemIR::BuiltinFunctionKind::IntConvertFloatChecked: {
CARBON_CHECK(builtin_kind.IsCompTimeOnly(
context.sem_ir(), arg_ids,
context.sem_ir().insts().Get(inst_id).type_id()));
CARBON_FATAL("Missing constant value for call to comptime-only function");
}
```
---
### Step 5: Standard Library Prelude Integration
Map the standard library primitive interfaces to your newly minted named
builtins under [core/prelude/](../../../core/prelude/):
- **Primitive Mappings**: Bind Carbon methods directly to string-literal
builtin equivalents:
```carbon
fn Convert[self: Self]() -> Float(To) = "int.convert_float";
```
- **Strict Orphan Rule Compliance**: Carbon's orphan rules prohibit
implementing interfaces where neither the type nor the interface is locally
defined in the backing source module.
- **Literal Conversions**: Literal types (like `FloatLiteral()`,
`IntLiteral()`) do not have backing Carbon source files. Therefore, an
`impl` of `UnsafeAs` (which is defined in `as.carbon`) between two
literal types must reside inside `as.carbon` itself.
- **Sized Conversions**: Implementations targeting sized primitives (e.g.
`Int(N)`, `Float(N)`) must reside in their respective type source files
(such as [int.carbon](../../../core/prelude/types/int.carbon) or
[float.carbon](../../../core/prelude/types/float.carbon)) where the
backing target type resides to prevent duplicate symbols and structural
recursion loops.
---
## High-Fidelity Validation & Test Authoring
Follow the [Toolchain tests](../toolchain_tests/SKILL.md) skill with specialized
patterns for builtins:
### 1. Checker Builtin File Splits
Create validation splits under
[toolchain/check/testdata/builtins/](../../../toolchain/check/testdata/builtins/):
- **Min-Prelude Limitations**: Standard operators (like `+`, `-`, `/`, `<`,
etc.) are **not** available in minimized preludes because the core operators
library isn't imported. To write tests with a minimal footprint, call
primitive builtins directly (e.g. `float.negate`, `float.div`) inside your
test code to build expressions.
- **Canonicalized Float Comparison**: In SemIR, real literal representations
with identical mathematical values can result in mismatched `RealId` objects
based on spelling variations. Verify compile-time constant conversions using
canonicalized comparison functions (e.g. passing converted results through
`Expect(X as f64)`) to completely avoid spelling mismatches in expected
outputs.
- **Locals Bypass**: If validating generic implicit conversions, compile-time
arguments cannot take local runtime variable parameters. Validate
compile-time conversions by passing literal constants directly, and sized
variable implicit conversions at runtime.
### 2. Machine Codegen Lowering Splits
Create testing splits under
[toolchain/lower/testdata/builtins/](../../../toolchain/lower/testdata/builtins/):
- Emplace a simple carbon binding to the tested builtin.
- Confirm matching LLVM metadata target definitions are mapped precisely
(e.g., matching `sitofp i32 %a to float`, `fptosi float %a to i32`).
+255
View File
@@ -0,0 +1,255 @@
---
name: Diagnostics
description:
Instructions for declaring, formatting, emitting, testing, and styling
diagnostic messages (errors, warnings, notes) in the Carbon toolchain.
---
# Diagnostics in the Carbon Toolchain
<!--
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
-->
The Carbon compiler features a highly-engineered, context-aware diagnostics
framework designed to deliver precise, readable, and highly targetable
diagnostic output (errors, warnings, notes). This document establishes strict
rules for declaring, formatting, emitting, testing, and styling compiler
diagnostics.
---
## Architecture Overview
```mermaid
graph TD
Kind[kind.def Registry] -->|Registration| Enum[Kind Enum ID]
Enum -->|Build/Emit| Emitter[Emitter LocT]
Emitter -->|ConvertLoc| Loc[Converted Physical Loc]
Emitter -->|formatv serialization| Formatting[format_providers.h / Custom Types]
Emitter -->|Emit Messages| Consumer[Console / Sorting Consumer]
Consumer -->|stable sort| StdErr[Compiler Standard Error]
```
Diagnostics are handled via three decoupled core components:
1. **Registry**: Globally enumerated kinds inside
[kind.def](../../../toolchain/diagnostics/kind.def).
2. **Emitters**: Specialized formatting pipelines (parameterized on custom
phase location types `LocT` like `Token` or `LocId`) that convert raw tokens
to standardized physical source locations (file, line, column, and text
snippet).
3. **Consumers**: Pipelines that process, track, filter, and sort diagnostics.
The default `SortingConsumer` buffers and stable-sorts diagnostics based on
their `last_byte_offset` matching compiler traversal order to ensure perfect
causal ordering.
---
## 1. Declaring and Registering Diagnostics
All diagnostic types must pass structural uniqueness and coverage verifications.
### The Diagnostic Registry
Every diagnostic kind must be registered globally as an enum option under
[kind.def](../../../toolchain/diagnostics/kind.def):
```cpp
// toolchain/diagnostics/kind.def
CARBON_DIAGNOSTIC_KIND(RealLiteralTooLargeForUnsizedInt)
```
### The Uniqueness Rule
To ensure optimal compile-time and analysis integrity, every diagnostic kind
declared in `kind.def` **MUST** be mapped to **one and only one** C++ macro
declaration (`CARBON_DIAGNOSTIC` or `CARBON_DIAGNOSTIC_ON_SCOPE`).
- **DO NOT** duplicate diagnostic definitions across different locations.
- The C++ representation of the diagnostic is a static/global constant of type
`DiagnosticBase<Args...>`.
- **Local Scope (Recommended)**: If the diagnostic is unique to a single
block/function body, declare it **locally** inside the function body
adjacent to its `Emit` trigger:
```cpp
void ConvertFloatValueToInt(...) {
CARBON_DIAGNOSTIC(FloatNaNConvertedToInt, Error,
"cannot convert NaN to integer type {0}", SemIR::TypeId);
context.emitter().Emit(loc_id, FloatNaNConvertedToInt, dest_type_id);
}
```
- **File Scope**: If the diagnostic is shared among multiple functions inside
the _same_ file, declare it at **file scope** inside the anonymous namespace
of the `.cpp` file.
- **Global Scope**: If a diagnostic (such as a shared helper note) is reused
_across different physical files_, define it in a shared header (e.g.
context/check helpers) and mark it `extern` where applicable, ensuring the
macro is only invoked once.
---
## 2. Formatting Diagnostic Arguments
Carbon diagnostics leverage LLVM's `formatv` engine. Parameters must be passed
using strongly-typed arguments to preserve translation capability.
### String Lifetimes & Pitfalls
- **`llvm::StringRef` is DISALLOWED**: Do not pass `StringRef` as a parameter
type to `CARBON_DIAGNOSTIC` due to unsafe lifetime and buffer-allocation
boundaries.
- **`llvm::StringLiteral` is DISALLOWED**: Do not use literal types as
arguments as they prevent future diagnostic localization and translations.
- **Use `std::string`**: If string formatting or custom allocations are
required, declare the parameter storage type as `std::string`.
### Format Selectors (`format_providers.h`)
Use specialized formatting wrappers under
[format_providers.h](../../../toolchain/diagnostics/format_providers.h) to
express clean inline options in format strings:
| Wrapper | Target Format Style | Example Usage | Output |
| :------------------------- | :------------------------------ | :----------------------------- | :------------------------------------------------------------------ |
| **`BoolAsSelect`** | `{Index:true\|false}` | `"{0:is signed\|is unsigned}"` | Maps bool to selection string. |
| **`IntAsSelect`** | `{Index:=Val:String\|:Default}` | `"{0:=1:is\|:are}"` | Matches exact options. |
| **`IntAsSelect` (Plural)** | `{Index:s}` | `"{0} argument{0:s}"` | Prints `"s"` if value != 1 (e.g., `"1 argument"`, `"3 arguments"`). |
### Custom Toolchain Type Mappings
Custom structures can define how they serialize inside diagnostics using the
`DiagnosticType` tag mapping to `Diagnostics::TypeInfo<StorageType>`:
- **Identifiers & Names** (declared in `check/diagnostic_helpers.h`):
- `NameId`: Formats raw identifier spelling, safely escaping keyword
conflicts under backticks automatically.
- `LibraryNameId`: Formats custom library descriptors cleanly (e.g.
`default library` or `library "foo"`).
- **Sized Primitives**:
- `TypedInt`: Formats an `APInt` constant exactly, extracting target
signedness representation automatically from its bound type
representation.
- **Type Formatter Hierarchy**: When choosing parameter types to print
compiler type representations, follow this priority list:
1. **`TypeOfInstId` (Preferred)**: Resolves the backing type of an
`InstId`, preserving programmatic aliasing, constraints, and source
spelling context. Enclosed under backticks automatically.
2. **`InstIdAsType`**: Converts an `InstId` for a type expression, printing
custom type layouts under backticks.
3. **`TypeId` (Fallback)**: Canonical description of the type. **Avoid when
possible** because type canonicalization loses intermediate source
program spelling and aliasing metadata.
4. **`*AsRawType` (e.g. `InstIdAsRawType`, `TypeIdAsRawType`)**: Formats
the type layout exactly like their counter-structures above, but
**omits** enclosing backticks (useful when inserting types inside larger
code snippets).
---
## 3. Fluent Emission Builders & RAII Scopes
### Fluent Builder Pattern
For compound diagnostics requiring multiple sub-notes, carets, or custom code
overrides, use `Build` to chain actions fluently:
```cpp
context.emitter()
.Build(second_node, ModifierRepeated, context.token_kind(second_node))
.Note(first_node, ModifierPrevious, context.token_kind(first_node))
.OverrideSnippet("custom snippet...")
.Emit();
```
> [!SAFETY] Emitter builders are marked `[[nodiscard]]`. To prevent a developer
> from creating a builder but failing to terminal-chain `.Emit()`, the builder
> uses an rvalue overload `Emit() &&` that triggers a compile-time
> `static_assert(false)`. You must save the builder to an lvalue or execute the
> chain exactly as `emitter.Build(...).Note(...).Emit()`.
### RAII Context & Annotation Scopes
Manage large checking structures requiring blanket note context using RAII block
scopes:
- `ContextScope`: Automatically converts any diagnostics emitted within its
scope into sub-notes under a high-level operation descriptor:
```cpp
ContextScope context_scope(&context.emitter(), [&](ContextBuilder& builder) {
builder.Context(eval_loc, InCallToEvalFn);
});
// any checker error emitted here will automatically append the 'InCallToEvalFn' note
```
- `AnnotationScope`: RAII block scope that automatically attaches blanket note
annotations to all scoped diagnostics.
---
## 4. Diagnostics Wording Style Guide
Refer to the official
[Diagnostic message style guide](../../../toolchain/docs/diagnostics.md#diagnostic-message-style-guide)
for complete details.
To maintain message consistency and integrate cleanly with Clang diagnostics in
interoperable code, adhere strictly to these rules:
- **Start with lowercase and omit periods**: Start diagnostic messages with a
lowercase letter or quoted code, and do **not** end them with a period
(e.g., `"cannot convert..."` or ``"`self` declared..."``).
- **Use backticks for quoted code**: Enclose identifiers, code constructs, and
types inside standard backticks (e.g., ``"`{0}` is bad"``).
- **Phrase as bullet points without articles**: Phrase diagnostics as
descriptive bullet points or sentence fragments rather than full sentences.
Leave out standard articles (`a`, `an`, `the`) unless necessary for logical
clarity. Semicolons can be used to separate fragments within a message.
- **Describe the situation and language rule**: Diagnostics should describe
the exact situation the toolchain observed. The language rule violated can
be mentioned if it wouldn't otherwise be clear:
- _Situation-only_: `"redeclaration of X"` (implies that redeclaration is
not permitted).
- _Rule-inclusion_:
``"`self` declared in invalid context; can only be declared in implicit parameter list"``.
- **Wording Choice ("cannot" vs "allowed")**: Explicitly avoid `"allowed"`,
`"legal"`, `"permitted"`, `"valid"`, and related passive wording. You may
use `"cannot"` if needed, but try to use phrasing that does not require it:
- _Correct_: ``"`export` in `impl` file"`` (Avoids `"allowed"`)
- _Incorrect_: ``"`export` is only allowed in API files"``
- _Correct_: ``"`extern library` specifies current library"`` (Avoids
`"cannot"`)
- _Incorrect_: ``"`extern library` cannot specify the current library"``
- **Developer Intent Hints**: It is acceptable for a diagnostic to guess at
the developer's intent and provide a hint _after_ explaining the situation
and the rule, but never as a substitute for that:
- _Correct_:
``"cannot implicitly convert `i32` to `String`; add `as String` for explicit conversion"``
- _Incorrect_: ``"add `as String` to convert `i32` to `String`"`` (Lacks
the core violation message).
- **Structure for Tooling API**: Try to structure diagnostics such that
parameter inputs can be programmatically extracted without string parsing
(prefer strongly-typed parameters over format placeholders where possible).
---
## 5. Diagnostics Testing & Coverage Verification
Carbon strictly enforces testing coverage at build-time.
1. **Tag Verification Requirement**: Every diagnostic kind declared in
`kind.def` (which is not blacklisted in the `UntestedKinds` array under
[coverage_test.cpp](../../../toolchain/diagnostics/coverage_test.cpp))
**MUST** be verified by at least one testcase file inside
`toolchain/*/testdata/`.
2. **Stderr Checklist Matchers**: The testcase split verifying the diagnostic
must catch it using standard CHECK matchers, explicitly tracking the
matching enum tag in standard error comments:
```carbon
// CHECK:STDERR: fail_bounds.carbon:[[@LINE+1]]:15: error: cannot convert NaN to integer type `i32` [FloatNaNConvertedToInt]
let a: i32 = Convert(nan_val);
```
3. **Build Enforcement**: Failing to provide a diagnostic test check matcher
triggers a build compilation error on the target test
`//toolchain/diagnostics:coverage_test`.
+30
View File
@@ -32,3 +32,33 @@ To validate a specific list of files:
```bash
pre-commit run --files <files>
```
## Command line tools restrictions
AI assistants **MUST NOT** use legacy or generic UNIX shell search/edit commands
when specialized environment tools exist.
- **DO NOT USE**: `cat`, `less`, `grep`, `sed`, or other shell utilities for
viewing, searching, or modifying files.
- **DO NOT USE**: `patch` to write and apply patch files.
- **DO NOT USE**: Writing custom scripts in other languages to circumvent this
limitation.
- **DO USE**: High-fidelity semantic API tools:
- Viewing: Use `view_file` instead of `cat` / `less`.
- Searching: Use `grep_search` / `find_by_name` instead of `grep` /
`find`.
- Modifying: Use `replace_file_content`, `multi_replace_file_content`, or
`write_to_file` instead of `sed` / `patch` / `python` edits.
You may only write and run temporary programs to modify source code if no
semantic tool is applicable or when performing complex, systematic transforms
across many codebase directories simultaneously.
## Temporary files management
Temporary files and scratchpad test scripts created by the assistant during
analysis, experiments, or debugging:
- **MUST** reside within the `tmp/` subdirectory under the workspace root.
- **MUST** be periodically cleaned out and deleted before ending your turn to
preserve a clean git workspace.
@@ -31,6 +31,10 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- Refer to [Toolchain Idioms](/toolchain/docs/idioms.md) for a
comprehensive list of patterns (for example, `ValueStore`, formatting
`.def` files, struct reflection) used throughout the implementation.
- **Builtin Functions**: Refer to the **Builtin functions** skill
([SKILL.md](../builtins/SKILL.md)) for guidelines on registering, mapping,
constant evaluating, and lowering compiler builtin primitives (e.g.
`"int.convert_float"`).
- **Phases**: Lex -> Parse -> Check -> Lower.
- **Definitions**: Many kinds (tokens, parse nodes, SemIR instructions) are
defined in `.def` files and expanded by way of macros.
@@ -68,6 +72,10 @@ script:
## Debugging and diagnostics
- **Compiler Diagnostics**: Refer to the **Diagnostics** skill
([SKILL.md](../diagnostics/SKILL.md)) for strict rules on declaring,
formatting, emitting, testing, and styling compiler diagnostic messages
(errors, warnings, notes).
- **Printing to stderr**: Use `llvm::errs() << "debug info\n";`.
- Avoid `std::cout` (it may interfere with tool output).
- **SemIR Stringification**:
@@ -86,6 +94,20 @@ script:
- **`llvm::Expected<T>`**: Similar to `ErrorOr`, used when interfacing with
LLVM.
### Context-Aware Diagnostics
When declaring and emitting errors, ensure semantic wording matches the exact
context:
- **Semantic Precision**: Do not reference "types" when raising errors for
unsized expressions like `IntLiteral()` or `FloatLiteral()`. For example,
use `RealLiteralTooLargeForUnsizedInt` instead of a diagnostic referencing
an "integer type".
- **Wording Consistency**: Before declaring a new diagnostic in
[kind.def](../../../toolchain/diagnostics/kind.def), search for existing
diagnostics in the targeted implementation files (for example, other uses of
`MaxIntWidth`) to align message structures and parameter expectations.
### Casting (LLVM style)
- Use `llvm::cast<T>(obj)` (checked, asserts on failure).
@@ -93,6 +115,16 @@ script:
- Use `llvm::isa<T>(obj)` (boolean check).
- **Avoid** `dynamic_cast` and standard RTTI.
### Leverage LLVM APIs
Before implementing custom algorithms for mathematical, logical, or bitwise
operations, inspect target LLVM ADT class APIs:
- **Builtin APIs**: Verify if LLVM classes (such as `APInt`, `APFloat`, or
`APSInt`) already offer native equivalents (for example, `.pow()`,
`ilogb()`, `.changeSign()`, `convertFromAPInt()`). Avoid duplicate, naive,
or inefficient custom loops.
### Data structures
- Prefer APIs in `common/` and `toolchain/base/` over LLVM ADTs. For example,
@@ -113,3 +145,16 @@ script:
`clang-format`).
5. **Parse node order**: Semantics processes parse nodes in post-order; ensure
your parser transitions support this.
6. **Builtin implementation gaps**: If adding a primitive builtin function,
make sure you address all phases of the lifecycle: macro definition
registration, signature validation, compile-time constant evaluation
(interpreter), LLVM IR lowering, and prelude modular implementation bindings
(avoiding orphan rules). Refer to the **Builtin functions** skill
([SKILL.md](../builtins/SKILL.md)) for details.
7. **Premature helper abstraction**: Avoid extracting tiny helper functions
that are called from exactly one place and do not significantly modularize
complex code. Prefer inlining directly to keep the implementation compact,
readable, and localized.
8. **Redundant bounds calculations**: Avoid repeating calculations of complex
boundary estimations (such as lower and upper bound estimations). Refactor
the logic to calculate unified values once, preserving compactness.
+47
View File
@@ -53,6 +53,12 @@ prelude file using `// INCLUDE-FILE`. Usually, include
`primitives.carbon`. This significantly speeds up execution and minimizes STDOUT
noise.
- **Builtin Primitive Testing**: Standard operators (such as `+`, `-`, `/`,
`<`, etc.) are **not** imported or available inside minimized preludes. To
write tests with a minimal prelude footprint, call primitive builtins
directly (e.g., `float.negate`, `float.div`) inside your test code to build
expressions.
### Split Tests and `[[@TEST_NAME]]`
A single physical file can test multiple scenarios using split constraints:
@@ -97,6 +103,47 @@ may omit `fail_` if it contains a least one split that has a `fail_` prefix.
Both the `fail_` and `todo_` prefixes are stripped from filename properties like
`[[@TEST_NAME]]`.
### Constant Evaluation Validation
When testing constant evaluation in semantic checker tests, follow these
conventions to ensure diagnostic stability and accuracy:
- **Literal Spelling Canonicalization**: In Semantic IR, real literals
(floating-point constants) with identical mathematical values can be
assigned distinct internal representation identifiers based on spelling
variations in source code. To completely prevent literal spelling mismatches
in expected output checks, validation tests must be performed using
canonical comparison methods (for example, passing converted values through
an `Expect(X as f64)` function).
- **Generic Parameters Validation**: To bypass compile-time constraints where
local runtime variables are rejected as generic function arguments, test
generic type conversions at runtime, and validate compile-time conversions
by passing static literal values directly into primitive builtin calls.
- **Exhaustive Edge Case Verification**: For complex mathematical algorithms
(such as floating-point to integer truncation and rounding), map and execute
test constraints covering every code branch, conditional exit, and fallback
evaluation path.
- **Rounding Threshold Boundaries**: Test cases that land extremely close to
mathematical boundaries (for example, floating-point literals representing a
tiny fraction above 1.0, such as $2^{30} \times 2^{-30}$ or
$10^{10} \times 10^{-10}$, verifying correct exact truncation down to 1 or
0).
- **Precise Float Literal Spelling**: Spell floating-point literals in test
code with exact mathematical precision targeting target thresholds. For
example, if testing the smallest fractional increment above 1.0, use the
exact hex fractional representation (e.g. `0x1.0000000000001p0`) or a highly
precise decimal fractional spelling (e.g. `1.0000000000000001`) instead of
coarse fractions like `1.1` to ensure correct boundary assertions.
- **Representation Capacity Boundaries**: Explicitly target edge cases near
representation limits of target types. Test combinations of mantissas and
exponents that yield values exactly on, just below, or just above the
capacity limits of fixed-size destination types (e.g. signed/unsigned
targets like `i32` or `u32`).
- **Zero-Value Sizing Bounds**: Verify boundary inputs of `0` and `0.0`
explicitly. Assert that zero inputs are sized and simplified correctly
without triggering calculation underflows, division-by-zero errors, or
underestimating required bit allocations.
### Test Code Comments
- **No agent thinking:** Do not include comments describing your reasoning or
+3 -28
View File
@@ -1,4 +1,4 @@
# Gemini & AI assistant guide for Carbon
# Gemini & AI Assistant Guide for Carbon
<!--
Part of the Carbon Language project, under the Apache License v2.0 with LLVM
@@ -6,24 +6,14 @@ Exceptions. See /LICENSE for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-->
This document provides high-density technical context for AI assistants (and
humans!) contributing to the Carbon Language project. If you are an AI
assistant, **read this first** to avoid common pitfalls.
## Table of contents
- [General instructions](#general-instructions)
- [Project structure](#project-structure)
- [Bazel usage](#bazel-usage)
- [Toolchain development](#toolchain-development)
This document provides high-density technical context for AI assistants
contributing to the Carbon Language project.
## General instructions
- **Communication**: Be concise, professional, and technical. Use GitHub-style
markdown.
- **Verification**: Always run relevant tests.
- **Tool usage**: Use web search for any research outside the immediate
codebase or KIs.
## Project structure
@@ -36,23 +26,8 @@ assistant, **read this first** to avoid common pitfalls.
- **[`toolchain/`](toolchain/)**: The C++ implementation of the compiler
(Toolchain).
## Tool usage
See the "Tool usage" skill for instructions on what tools to use in the
carbon-lang project.
## Bazel usage
> [!IMPORTANT] Always use `bazelisk` instead of `bazel` for all commands in the
> Carbon project. Refer to the
> [Bazel usage skill](/.agents/skills/bazel/SKILL.md) for detailed instructions.
## Code style
See the "Code style" skill for instructions on formatting, style guides, and
code conventions to follow.
## Toolchain development
See the "Toolchain Development" skill for instructions on architecture,
building, testing, debugging, C++ patterns, and common pitfalls.