mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 18:11:08 +01:00
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:
@@ -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`).
|
||||
@@ -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`.
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user