From 753179c001ce750195b85bf7bc680cca0558eb34 Mon Sep 17 00:00:00 2001 From: Adam Getchell Date: Mon, 24 Aug 2026 09:38:55 -0700 Subject: [PATCH 1/2] fix(tooling): enforce the f64 algebraic-operation ban - Reject algebraic f64 calls and function items across repository-owned Rust while preserving ordinary operators and `mul_add`. - Document the numerical contract, MSRV, release-pinned references, and native BLAS alternatives. - Refresh contributor tool pins and adapt subprocess encoding to Ty 0.0.74. --- AGENTS.md | 9 ++++ Cargo.lock | 8 ++-- README.md | 29 ++++++++---- justfile | 4 +- pyproject.toml | 2 +- scripts/subprocess_utils.py | 4 +- semgrep.yaml | 14 +++++- .../src/project_rules/algebraic_float.rs | 22 ++++++++-- uv.lock | 44 +++++++++---------- 9 files changed, 90 insertions(+), 46 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 264ee03..8726366 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -213,6 +213,15 @@ When user requests commit message generation: - The current MSRV and pinned contributor/CI toolchain are Rust 1.98.0. Keep `Cargo.toml`, `rust-toolchain.toml`, and `clippy.toml` aligned when that baseline changes deliberately. +- Rust's `f64::algebraic_*` operations are forbidden in all repository-owned + Rust code, including tests, examples, and benchmarks. + Their unspecified reassociation, precision, and special-value behavior can + invalidate defined operation order, error bounds, non-finite classification, + exact fallbacks, and reproducibility. Ordinary operators remain allowed. + Deliberate fused multiply-add through `f64::mul_add` is explicitly allowed; + existing numerical kernels and error bounds may rely on its single-rounding + evaluation. Any fast-math design requires a separate issue, opt-in contract, + correctness analysis, and benchmark evidence. - Prefer borrowed APIs by default: take references (`&T`, `&mut T`, `&[T]`) as arguments and return borrowed views (`&T`, `&[T]`) when possible. Only take ownership or return `Vec`/allocated data when required. diff --git a/Cargo.lock b/Cargo.lock index a7ae053..26897d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,7 +91,7 @@ checksum = "fc0e56a716f1e132ff6bf4bdac1c944a3fcdc1cae65f70a4a2a1ac3b401d2d1f" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -1204,7 +1204,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -1268,9 +1268,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", diff --git a/README.md b/README.md index 7cc77a7..8ec78c7 100644 --- a/README.md +++ b/README.md @@ -42,22 +42,28 @@ With `features = ["exact"]`, stored binary64 inputs are lifted losslessly to rationals for exact determinant signs, determinant values, and solves. Exactness starts at the stored values and cannot recover information rounded away before construction. See the -[mathematical basis](https://github.com/acgetchell/la-stack/blob/main/docs/mathematical_basis.md) +[mathematical basis](https://github.com/acgetchell/la-stack/blob/v0.4.5/docs/mathematical_basis.md) for the algorithms, validity boundaries, and supporting references. ## ✨ Design goals -- ✅ `Copy` types where possible -- ✅ Const-generic storage (no dynamically sized matrix or vector representation) - ✅ `const fn` where possible (compile-time evaluation of determinants, dot products, etc.) -- ✅ Explicit algorithms (LU, solve, determinant) +- ✅ Const-generic storage (no dynamically sized matrix or vector representation) +- ✅ `Copy` types where possible +- ✅ Defined binary64 arithmetic semantics: Rust's `f64::algebraic_*` + operations are forbidden because their unspecified reassociation, precision, + and special-value behavior is incompatible with the crate's error bounds, + non-finite classification, exact fallbacks, and reproducibility contract; + deliberate `f64::mul_add` remains allowed for its defined single-rounding + semantics - ✅ Error-bounded f64 determinant filtering plus optional exact signs (`det_errbound`, `det_sign_exact`) - ✅ Exact determinant values and linear solves via optional arbitrary-precision arithmetic (`det_exact`, `solve_exact`, strict/rounded f64 conversions) -- ✅ No runtime dependencies by default (optional features may add deps) +- ✅ Explicit algorithms (LU, solve, determinant) - ✅ Inline, stack-backed storage for core types; optional arbitrary-precision exact values allocate as required +- ✅ No runtime dependencies by default (optional features may add deps) - ✅ `unsafe` forbidden See [CHANGELOG.md](https://github.com/acgetchell/la-stack/blob/v0.4.5/CHANGELOG.md) @@ -67,11 +73,14 @@ for current release planning. ## 🚫 Anti-goals -- Bare-metal performance: see [`blas-src`](https://crates.io/crates/blas-src), - [`lapack-src`](https://crates.io/crates/lapack-src), or [`openblas-src`](https://crates.io/crates/openblas-src) +- Alternate floating-point scalar families: `la-stack` supports `f64` and optional exact arithmetic, not `f32` / `f16` APIs +- Bare-metal performance: use [`blas`](https://crates.io/crates/blas) or + [`lapack`](https://crates.io/crates/lapack) with a native backend selected + through [`blas-src`](https://crates.io/crates/blas-src), + [`lapack-src`](https://crates.io/crates/lapack-src), or + [`openblas-src`](https://crates.io/crates/openblas-src) - Broad general-purpose linear algebra: use [`nalgebra`](https://crates.io/crates/nalgebra) - Large matrices/dimensions with parallelism: use [`faer`](https://crates.io/crates/faer) -- Alternate floating-point scalar families: `la-stack` supports `f64` and optional exact arithmetic, not `f32` / `f16` APIs ## ✅ Use this crate when @@ -98,6 +107,8 @@ cases better served by broader linear-algebra libraries. ## 🚀 Quickstart +The minimum supported Rust version (MSRV) is 1.98.0. + Add this to your `Cargo.toml`: ```toml @@ -445,7 +456,7 @@ fn main() -> Result<(), LaError> { The error coefficients (`ERR_COEFF_2`, `ERR_COEFF_3`, `ERR_COEFF_4`) are conservative, dimension-specific constants, not caller-tunable tolerances. The -[mathematical basis](https://github.com/acgetchell/la-stack/blob/main/docs/mathematical_basis.md#determinants-and-certified-sign-filtering) +[mathematical basis](https://github.com/acgetchell/la-stack/blob/v0.4.5/docs/mathematical_basis.md#determinants-and-certified-sign-filtering) documents the bound and states its range preconditions. The constants are explicit crate-root exports for advanced users who want to compose the same bound: `use la_stack::{ERR_COEFF_2, ERR_COEFF_3, ERR_COEFF_4};`. They intentionally stay diff --git a/justfile b/justfile index 3cfa34b..d4b21c0 100644 --- a/justfile +++ b/justfile @@ -23,10 +23,10 @@ cargo_machete_version := "0.9.2" cargo_nextest_version := "0.9.143" cargo_update_version := "22.1.1" clippy_sarif_version := "0.8.0" -dprint_version := "0.56.0" +dprint_version := "0.56.1" git_cliff_version := "2.13.1" just_version := "1.58.0" -rumdl_version := "0.2.58" +rumdl_version := "0.2.60" sarif_fmt_version := "0.8.0" taplo_version := "0.10.0" typos_version := "1.49.0" diff --git a/pyproject.toml b/pyproject.toml index 5de644d..ceb2add 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -155,6 +155,6 @@ dev = [ "semgrep==1.174.0", "shellcheck-py==0.11.0.1", "shfmt-py==4.0.0", - "ty==0.0.73", + "ty==0.0.74", "yamllint==1.38.0", ] diff --git a/scripts/subprocess_utils.py b/scripts/subprocess_utils.py index 0045afd..e816a76 100644 --- a/scripts/subprocess_utils.py +++ b/scripts/subprocess_utils.py @@ -331,8 +331,8 @@ def run_git_command_with_input( """ git_path = get_safe_executable("git") run_kwargs = _build_run_kwargs("run_git_command_with_input", **kwargs) - encoding: str = run_kwargs.get("encoding") or "utf-8" - errors: str = run_kwargs.get("errors") or "strict" + encoding = cast("str", run_kwargs.get("encoding") or "utf-8") + errors = cast("str", run_kwargs.get("errors") or "strict") payload = input_data if isinstance(input_data, bytes) else input_data.encode(encoding, errors) with tempfile.TemporaryFile() as stdin: stdin.write(payload) diff --git a/semgrep.yaml b/semgrep.yaml index aef8835..8878e4b 100644 --- a/semgrep.yaml +++ b/semgrep.yaml @@ -100,15 +100,25 @@ rules: - "/src/**/*.rs" - "/examples/**/*.rs" - "/benches/**/*.rs" - # Test-only violation fixture: `just semgrep` excludes it, while + # Includes integration/property tests and the deliberate violation + # fixture. `just semgrep` excludes that fixture, while # `just semgrep-test` scans it directly. - - "/tests/semgrep/src/project_rules/algebraic_float.rs" + - "/tests/**/*.rs" pattern-either: - pattern: $VALUE.algebraic_add(...) - pattern: $VALUE.algebraic_sub(...) - pattern: $VALUE.algebraic_mul(...) - pattern: $VALUE.algebraic_div(...) - pattern: $VALUE.algebraic_rem(...) + - patterns: + - pattern: $FLOAT::$METHOD + - metavariable-regex: + metavariable: $FLOAT + regex: ^f64$ + - metavariable-regex: + metavariable: $METHOD + regex: ^algebraic_(?:add|sub|mul|div|rem)$ + - pattern-not-inside: $FLOAT::$METHOD(...) - id: la-stack.rust.no-public-infallible-raw-f64-constructors languages: diff --git a/tests/semgrep/src/project_rules/algebraic_float.rs b/tests/semgrep/src/project_rules/algebraic_float.rs index 5fec58b..277a625 100644 --- a/tests/semgrep/src/project_rules/algebraic_float.rs +++ b/tests/semgrep/src/project_rules/algebraic_float.rs @@ -13,14 +13,19 @@ pub fn forbidden_f64_operations(left: f64, right: f64) -> [f64; 5] { ] } -pub fn forbidden_f32_associated_operation(left: f32, right: f32) -> f32 { +pub fn forbidden_f64_associated_operation(left: f64, right: f64) -> f64 { // ruleid: la-stack.rust.no-algebraic-float-operations - f32::algebraic_add(left, right) + f64::algebraic_add(left, right) } -pub fn forbidden_f64_associated_operation(left: f64, right: f64) -> f64 { +pub fn forbidden_f64_function_item() -> fn(f64, f64) -> f64 { // ruleid: la-stack.rust.no-algebraic-float-operations - f64::algebraic_add(left, right) + f64::algebraic_sub +} + +pub fn forbidden_f64_reduction(values: &[f64]) -> Option { + // ruleid: la-stack.rust.no-algebraic-float-operations + values.iter().copied().reduce(f64::algebraic_mul) } pub fn permitted_f64_operations(left: f64, right: f64) -> [f64; 6] { @@ -39,3 +44,12 @@ pub fn permitted_f64_operations(left: f64, right: f64) -> [f64; 6] { left.mul_add(right, 1.0), ] } + +pub fn permitted_f64_function_item() -> fn(f64, f64) -> f64 { + fn add(left: f64, right: f64) -> f64 { + left + right + } + + // ok: la-stack.rust.no-algebraic-float-operations + add +} diff --git a/uv.lock b/uv.lock index 83fe52f..61799f2 100644 --- a/uv.lock +++ b/uv.lock @@ -478,7 +478,7 @@ dev = [ { name = "semgrep", specifier = "==1.174.0" }, { name = "shellcheck-py", specifier = "==0.11.0.1" }, { name = "shfmt-py", specifier = "==4.0.0" }, - { name = "ty", specifier = "==0.0.73" }, + { name = "ty", specifier = "==0.0.74" }, { name = "yamllint", specifier = "==1.38.0" }, ] @@ -1177,27 +1177,27 @@ wheels = [ [[package]] name = "ty" -version = "0.0.73" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/90/c4e1bb4cead3b644c3e258a27f9b05c7dc5eb0ec96a4f5282194edae9e0d/ty-0.0.73.tar.gz", hash = "sha256:823d4ce0d237bfc7eb6bcee70842f2c0706113813a16951077840743712f4b74", size = 6712739, upload-time = "2026-08-19T03:12:43.381Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/0f/f5e1801e55cc631f2db193276675b30561b963a2403da832bffb5d100267/ty-0.0.73-py3-none-linux_armv6l.whl", hash = "sha256:90a946082bf9bc446b5e72973d9f4ff1222a240b2ca4c9e6eed61eb913e30810", size = 12715452, upload-time = "2026-08-19T03:12:06.673Z" }, - { url = "https://files.pythonhosted.org/packages/54/32/515dd05074c213b433524ab97eb003b0132ae7e358e0d75633ba7a314ed8/ty-0.0.73-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b7d6b5c6a6db7ea95fbbc16af514ef44a27a29a2fe1dc798900790364d170209", size = 12301870, upload-time = "2026-08-19T03:12:08.924Z" }, - { url = "https://files.pythonhosted.org/packages/50/4d/085b4889f0d4bbe4af8b96242d4a1cb209fff95967cfa239ea141983719b/ty-0.0.73-py3-none-macosx_11_0_arm64.whl", hash = "sha256:dd6f657f463e01372d8688f235be164750c8db722c97da27fa4903aa8d40b203", size = 12111741, upload-time = "2026-08-19T03:12:11.067Z" }, - { url = "https://files.pythonhosted.org/packages/95/f6/d6ec277cadfecf03ad4c18551b67c4c6eb7807a0560d801db14be99d7a89/ty-0.0.73-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2de468e33fd44c9ff1c43473a7316f4289480f5cba8995a67b6d22aee39ca9", size = 12196124, upload-time = "2026-08-19T03:12:13.14Z" }, - { url = "https://files.pythonhosted.org/packages/75/b7/ce78d8707563af9cae9bbd25328bfbc4931035085bd20089adf0c418f70e/ty-0.0.73-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2942fa0ef795a66034cdc8d75a72f453442f3b58ff2f69b4da05b7b954765b55", size = 12488557, upload-time = "2026-08-19T03:12:15.252Z" }, - { url = "https://files.pythonhosted.org/packages/d8/e8/329b9851b23502758c5c98e8cc875ea2a1b4c9674b4ca3a86da56a5063d3/ty-0.0.73-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e0f1ef14f642e18ac4e7a616a2796dcf7a5d82e28cd17f9796494acc7c4aabb", size = 13215606, upload-time = "2026-08-19T03:12:17.225Z" }, - { url = "https://files.pythonhosted.org/packages/36/38/67fedfd2cb77516ef0066b1642f487dba0eb3006493cf3475b15f5b8b228/ty-0.0.73-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16981e15fdceedb37d0aff76c5ac25914595dfee2675af95335550064251ad22", size = 13665497, upload-time = "2026-08-19T03:12:19.286Z" }, - { url = "https://files.pythonhosted.org/packages/8e/b3/154f4dd48ec5eebc186ab4b822c6e62f982fc5ddfd262d6e3903c2acba44/ty-0.0.73-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:644b2bec8a2e2e4957a942ae81d6cff5571c489bb5a8675e4d3886de537a694d", size = 13351231, upload-time = "2026-08-19T03:12:21.353Z" }, - { url = "https://files.pythonhosted.org/packages/35/5f/d462496903fbe453fb76363f8478be929c8e6ff21e6928c57dcd7e5fa21f/ty-0.0.73-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:338d565be3186f50ff8e9d10483685549c2d23f0754485d5ede3b54f4319188a", size = 12782586, upload-time = "2026-08-19T03:12:23.667Z" }, - { url = "https://files.pythonhosted.org/packages/87/52/ec6d24b74abe3ec324204c1c71e6d0c6c76a17ffc15fd51d603b0a302abe/ty-0.0.73-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:11c7b6d839309d2c102cb3a4c03d817176bbfab5b2fccc95a75ec5c9597421c9", size = 13247134, upload-time = "2026-08-19T03:12:25.956Z" }, - { url = "https://files.pythonhosted.org/packages/26/20/cc74650fec56a54786c6d7c89e09576fcad3092be34cf21715d39a406a9b/ty-0.0.73-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:488572db7ff97fb50ea36a76250f2d617c9727d143da6c7bf0623276eb0fc507", size = 12309344, upload-time = "2026-08-19T03:12:28.122Z" }, - { url = "https://files.pythonhosted.org/packages/89/bd/4b0a9087f4315d7fbadf77a3ce44c816cc9ffabed1ced06cc5be81fbc414/ty-0.0.73-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1b958ebceefbbf594e59eb8d3d55bbd033ce634026fcba3e4bc3179e78e45bb7", size = 12502319, upload-time = "2026-08-19T03:12:30.128Z" }, - { url = "https://files.pythonhosted.org/packages/11/80/0a925074911fe111912ea29d9eed309bcc183f43d2fb3eef07db056a0beb/ty-0.0.73-py3-none-musllinux_1_2_i686.whl", hash = "sha256:91a32993b3c34e42c3f323ad6c0399cb596bd1c27e9b7f20db7cd64c1067b68e", size = 12753688, upload-time = "2026-08-19T03:12:32.433Z" }, - { url = "https://files.pythonhosted.org/packages/24/6b/aeccaf89efbc2e112bd415340a22e2669ec998aa397242503e747b712ca4/ty-0.0.73-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:bab8a19fbf51f479bddb2a12c5fabfe52f918a5590362321ed5d89b44eb62c15", size = 13069050, upload-time = "2026-08-19T03:12:35.398Z" }, - { url = "https://files.pythonhosted.org/packages/d7/3e/eae485fd86c1585943fd4e1746b0757b2da01e2c43136ebe8c686fe1c7f1/ty-0.0.73-py3-none-win32.whl", hash = "sha256:03347a612f0fa020b19bfd8dbd521db6ecc75d377a3e4d4f6e6c2e62871da4cc", size = 12053187, upload-time = "2026-08-19T03:12:37.565Z" }, - { url = "https://files.pythonhosted.org/packages/a7/01/9b8b983786e3ce34924e372e8b76b92b508273ab65c589fc7e88cc03ee17/ty-0.0.73-py3-none-win_amd64.whl", hash = "sha256:cedd05122ded0b5dcc55431a370e974b747f99c41c290a3d2ab8c1867f197519", size = 12693838, upload-time = "2026-08-19T03:12:39.483Z" }, - { url = "https://files.pythonhosted.org/packages/ea/88/25333bbfea6a5dc064371d2002d3d4807db90b84d5448f9106b2712b0fbc/ty-0.0.73-py3-none-win_arm64.whl", hash = "sha256:e47068f8369dea5d641a26a2ad0a947a320b02ff87099b07e95de0323245a4dc", size = 12443573, upload-time = "2026-08-19T03:12:41.449Z" }, +version = "0.0.74" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/0f/c767853e88567a2ec7e996dd95e3105b1bc62c95d103689311ef0f4a603c/ty-0.0.74.tar.gz", hash = "sha256:da14344fc8625fc9ff359bafb856ad575636ea86d9bb6a629b146bff27b380e6", size = 6786318, upload-time = "2026-08-22T15:05:54.054Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/95/6ded58bc97885c6d88fa1f9cd815031489200738f961cbf0466663213f80/ty-0.0.74-py3-none-linux_armv6l.whl", hash = "sha256:8969ef4e508debf00cf58f9ea85a539f799b1732c59cdfcecd037630b9755b30", size = 12790043, upload-time = "2026-08-22T15:05:05.015Z" }, + { url = "https://files.pythonhosted.org/packages/d9/8a/5e323603b6ab8731144421877ee8a0f8ac5a5511e67857127caa09f6730e/ty-0.0.74-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:51fb6cf5b98e1e1140825b2430943f78d744876a735231656eafbb4c3f7eca3c", size = 12371748, upload-time = "2026-08-22T15:05:08.609Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/ee72e08cb705281e8d8c42917dd577aa598a8a098008495fda5176ee3f6e/ty-0.0.74-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8ebe60b1f0a948c793d6c77fc9e9ddda599e4f023c04ab16e8e03bcb428c3fa0", size = 12282403, upload-time = "2026-08-22T15:05:11.448Z" }, + { url = "https://files.pythonhosted.org/packages/da/b3/fd935b694ff68bc278af50f7ad04770b36ce6306399baef7e1847b553a9d/ty-0.0.74-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa97f407a695c890a53615966a663c7d2167e2cabe88db7ca1a24d62635cdfc8", size = 12345164, upload-time = "2026-08-22T15:05:14.19Z" }, + { url = "https://files.pythonhosted.org/packages/54/5c/5b5825268e029ebb164c909780103dbbae367f069801410068bf1cef29b3/ty-0.0.74-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:673ddb733d4a0db31385ba1ed9ff1f6bd9dc5565413ce57b1ca5ac4c7803da5d", size = 12556646, upload-time = "2026-08-22T15:05:16.994Z" }, + { url = "https://files.pythonhosted.org/packages/56/e7/515914e571d62ce0101744fed3f881936eeb1b30dc37beb72b4f7ca1e289/ty-0.0.74-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1028e7c6b4f6145e9704552f43a5fffdcd51b42263ffdcd9c9677762bc395a4a", size = 13311653, upload-time = "2026-08-22T15:05:20.254Z" }, + { url = "https://files.pythonhosted.org/packages/b0/07/d1452babb6f9266c2122cabc095180b70ed306fb770b2996753814d2237d/ty-0.0.74-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79841a8890493021fb308772474983316eb91f7b56cb227a6a05a06b262a36f0", size = 13768284, upload-time = "2026-08-22T15:05:23.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/60/8d4a2fc7842a47210a1cb0a16a187d9de39ad5d509a00fb74c1c073afcde/ty-0.0.74-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94859d321f3c6a6c8f7bfc3f40e8319cda7e6e012e613440f3dfd145d5010e2e", size = 13422306, upload-time = "2026-08-22T15:05:26.248Z" }, + { url = "https://files.pythonhosted.org/packages/de/76/ebbc269a8c4efcc4d44624993bd188145f20d60ebda9680b15aaec42cc50/ty-0.0.74-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:970a8b2c09ff3be04c8a1c6767332d861be4fce85efe7bb205e4ade7c8655274", size = 12970637, upload-time = "2026-08-22T15:05:29.15Z" }, + { url = "https://files.pythonhosted.org/packages/9e/dd/b99f7236acbf856780ca1779a48143d2d9f2c24d7f531a0ce15a022b8a87/ty-0.0.74-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:795f763b3ded85574c2c2846a6fb8acf2aa76e9e83d761143e92b1f0c7ffa2cd", size = 13344891, upload-time = "2026-08-22T15:05:32.033Z" }, + { url = "https://files.pythonhosted.org/packages/0b/d7/9ff7449a4c7e6428f2c6f298e74cf24b70668f29d45c249507a723ff3782/ty-0.0.74-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:dc086db5367d912c31c0cc872deb7387290e779a4b9b54fcb944673a7cd52c7b", size = 12395272, upload-time = "2026-08-22T15:05:34.702Z" }, + { url = "https://files.pythonhosted.org/packages/b1/dd/b23a5b6b35d37df89dc8dc5daa09efd9245a668b50c4c81c25de21567dc1/ty-0.0.74-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0314d7b391cf684e47c2fa093d2ce4c597cfc9b01d9a315fe204aed6359b271b", size = 12573079, upload-time = "2026-08-22T15:05:37.683Z" }, + { url = "https://files.pythonhosted.org/packages/23/c5/ccba16239d6129533c8b3603458d0f4dd2ba69478e47059073968e74261d/ty-0.0.74-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4a45dd2e991e8bdae82ba78c8cd051b253f60bc71a6536598fa3ef580b4fc9b", size = 12832506, upload-time = "2026-08-22T15:05:40.505Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1c/2390912634dff4f341f97b397f2aee341ff062be0a66cda37d59375454f2/ty-0.0.74-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:210e2eac6b018fb934e2b8dac3956a0ba076a3fb1fa6f135058c825e5b759b81", size = 13154752, upload-time = "2026-08-22T15:05:43.355Z" }, + { url = "https://files.pythonhosted.org/packages/c4/33/a8c12188227e6f74f91853a7374e01ed81d6ad21c16c8b70e92dbebfe46a/ty-0.0.74-py3-none-win32.whl", hash = "sha256:db0bb6a8f098ef9bd1be861f73b4f7c0320d40d4c05c7ae0a8677d4e7aa4f6e5", size = 12130002, upload-time = "2026-08-22T15:05:46.058Z" }, + { url = "https://files.pythonhosted.org/packages/21/5c/064f28ccb9c234cfce5a2f7aa69a256663d5ae5bb0290b3a9706cc4d1e4c/ty-0.0.74-py3-none-win_amd64.whl", hash = "sha256:bebff181515255b3c78bd2e7693ae66fab6064ad4feea2065c68bc01022aa678", size = 12771435, upload-time = "2026-08-22T15:05:48.811Z" }, + { url = "https://files.pythonhosted.org/packages/fe/06/d6becdaca0315346c26b6df97cb0eafa81de4f870945d6989e88704374ed/ty-0.0.74-py3-none-win_arm64.whl", hash = "sha256:1a3469eaaf8c85b1c0a15bede25d36daea4b09fce1d913e965b24e24b3f1d6c6", size = 12558299, upload-time = "2026-08-22T15:05:51.543Z" }, ] [[package]] From 31021f1322a9c443fa4df63e632e8038600b2461 Mon Sep 17 00:00:00 2001 From: Adam Getchell Date: Mon, 24 Aug 2026 10:07:08 -0700 Subject: [PATCH 2/2] fix(tooling): detect qualified f64 algebraic operations - Reject `::algebraic_*` calls and function items. - Preserve qualified FMA calls and function items as allowed. --- semgrep.yaml | 11 ++++++++++ .../src/project_rules/algebraic_float.rs | 22 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/semgrep.yaml b/semgrep.yaml index 8878e4b..ec654ab 100644 --- a/semgrep.yaml +++ b/semgrep.yaml @@ -110,6 +110,17 @@ rules: - pattern: $VALUE.algebraic_mul(...) - pattern: $VALUE.algebraic_div(...) - pattern: $VALUE.algebraic_rem(...) + - patterns: + - pattern: ::$METHOD(...) + - metavariable-regex: + metavariable: $METHOD + regex: ^algebraic_(?:add|sub|mul|div|rem)$ + - patterns: + - pattern: ::$METHOD + - metavariable-regex: + metavariable: $METHOD + regex: ^algebraic_(?:add|sub|mul|div|rem)$ + - pattern-not-inside: ::$METHOD(...) - patterns: - pattern: $FLOAT::$METHOD - metavariable-regex: diff --git a/tests/semgrep/src/project_rules/algebraic_float.rs b/tests/semgrep/src/project_rules/algebraic_float.rs index 277a625..4c1fdde 100644 --- a/tests/semgrep/src/project_rules/algebraic_float.rs +++ b/tests/semgrep/src/project_rules/algebraic_float.rs @@ -23,6 +23,16 @@ pub fn forbidden_f64_function_item() -> fn(f64, f64) -> f64 { f64::algebraic_sub } +pub fn forbidden_f64_qualified_call(left: f64, right: f64) -> f64 { + // ruleid: la-stack.rust.no-algebraic-float-operations + ::algebraic_add(left, right) +} + +pub fn forbidden_f64_qualified_function_item() -> fn(f64, f64) -> f64 { + // ruleid: la-stack.rust.no-algebraic-float-operations + ::algebraic_sub +} + pub fn forbidden_f64_reduction(values: &[f64]) -> Option { // ruleid: la-stack.rust.no-algebraic-float-operations values.iter().copied().reduce(f64::algebraic_mul) @@ -53,3 +63,15 @@ pub fn permitted_f64_function_item() -> fn(f64, f64) -> f64 { // ok: la-stack.rust.no-algebraic-float-operations add } + +pub fn permitted_f64_qualified_fma( + left: f64, + right: f64, +) -> (f64, fn(f64, f64, f64) -> f64) { + ( + // ok: la-stack.rust.no-algebraic-float-operations + ::mul_add(left, right, 1.0), + // ok: la-stack.rust.no-algebraic-float-operations + ::mul_add, + ) +}