diff --git a/.agents/skills/builtins/SKILL.md b/.agents/skills/builtins/SKILL.md new file mode 100644 index 000000000000..07a4f1c526df --- /dev/null +++ b/.agents/skills/builtins/SKILL.md @@ -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 + + + +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`: 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", ValidateSignatureAnyFloat>}; + ``` + +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`). diff --git a/.agents/skills/diagnostics/SKILL.md b/.agents/skills/diagnostics/SKILL.md new file mode 100644 index 000000000000..f9444476a148 --- /dev/null +++ b/.agents/skills/diagnostics/SKILL.md @@ -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 + + + +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`. +- **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`: + +- **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`. diff --git a/.agents/skills/tool_usage/SKILL.md b/.agents/skills/tool_usage/SKILL.md index 06997420ce7d..cc28cd1b1170 100644 --- a/.agents/skills/tool_usage/SKILL.md +++ b/.agents/skills/tool_usage/SKILL.md @@ -32,3 +32,33 @@ To validate a specific list of files: ```bash pre-commit run --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. diff --git a/.agents/skills/toolchain_development/SKILL.md b/.agents/skills/toolchain_development/SKILL.md index fa763816f6d2..ea17f8a7b961 100644 --- a/.agents/skills/toolchain_development/SKILL.md +++ b/.agents/skills/toolchain_development/SKILL.md @@ -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`**: 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(obj)` (checked, asserts on failure). @@ -93,6 +115,16 @@ script: - Use `llvm::isa(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. diff --git a/.agents/skills/toolchain_tests/SKILL.md b/.agents/skills/toolchain_tests/SKILL.md index 569e97714b61..b006412a2c2a 100644 --- a/.agents/skills/toolchain_tests/SKILL.md +++ b/.agents/skills/toolchain_tests/SKILL.md @@ -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 diff --git a/AGENTS.md b/AGENTS.md index 383ef396e03b..6143ccabc90b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,4 @@ -# Gemini & AI assistant guide for Carbon +# Gemini & AI Assistant Guide for Carbon -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.