[clang] [clang] add no-op __builtin_static_analysis_assume (PR #162939)

Florian Mayer via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 10 16:08:28 PDT 2025


https://github.com/fmayer created https://github.com/llvm/llvm-project/pull/162939

This builtin can be used by user code to communicate with static
analyis tools (e.g. clang-tidy or clang-static-analyzer). Because the
arguments are unevaluated, it is suitable for use in macros, where
evaluating the same expression multiple times can change program
semantics.

RFC: https://discourse.llvm.org/t/rfc-builtin-static-analysis-assume/88544


>From 8c84fe63aac839472e4dff7e5daca03c1cb4ab8f Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer at google.com>
Date: Fri, 10 Oct 2025 16:08:14 -0700
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
 =?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.4
---
 clang/include/clang/Basic/Builtins.td         |  6 +++++
 clang/lib/CodeGen/CGBuiltin.cpp               |  2 ++
 .../test/AST/builtin-static-analysis-assume.c | 22 +++++++++++++++++++
 .../CodeGen/builtin-static-analysis-assume.c  | 17 ++++++++++++++
 .../Sema/builtin-static-analysis-assume.cpp   | 14 ++++++++++++
 5 files changed, 61 insertions(+)
 create mode 100644 clang/test/AST/builtin-static-analysis-assume.c
 create mode 100644 clang/test/CodeGen/builtin-static-analysis-assume.c
 create mode 100644 clang/test/Sema/builtin-static-analysis-assume.cpp

diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 792e2e07ec594..4f0e9129d98aa 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5253,3 +5253,9 @@ def CountedByRef : Builtin {
   let Attributes = [NoThrow, CustomTypeChecking];
   let Prototype = "int(...)";
 }
+
+def StaticAnalysisAssume : Builtin {
+  let Spellings = ["__builtin_static_analysis_assume"];
+  let Attributes = [NoThrow, Const, Pure, UnevaluatedArguments];
+  let Prototype = "void(bool)";
+}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 9ee810c9d5775..34e6b512d20c6 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -3733,6 +3733,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
 
     return RValue::get(Result);
   }
+  case Builtin::BI__builtin_static_analysis_assume:
+    return RValue::get(nullptr);
   case Builtin::BI__builtin_prefetch: {
     Value *Locality, *RW, *Address = EmitScalarExpr(E->getArg(0));
     // FIXME: Technically these constants should of type 'int', yes?
diff --git a/clang/test/AST/builtin-static-analysis-assume.c b/clang/test/AST/builtin-static-analysis-assume.c
new file mode 100644
index 0000000000000..ac544be49ab90
--- /dev/null
+++ b/clang/test/AST/builtin-static-analysis-assume.c
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -ast-dump -triple x86_64-linux-gnu %s \
+// RUN: | FileCheck %s --strict-whitespace --check-prefixes=CHECK
+//
+// Tests with serialization:
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-pch -o %t %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -include-pch %t -ast-dump-all /dev/null \
+// RUN: | FileCheck %s --strict-whitespace
+
+int fun() {
+    int x = 0;
+    __builtin_static_analysis_assume(++x >= 1);
+    return x;
+}
+
+// CHECK: |-CallExpr {{.*}} <line:11:5, col:46> 'void'
+// CHECK: | |-ImplicitCastExpr {{.*}} <col:5> 'void (*)(_Bool)' <BuiltinFnToFnPtr>
+// CHECK: | | `-DeclRefExpr {{.*}} <col:5> '<builtin fn type>' Function {{.*}} '__builtin_static_analysis_assume' 'void (_Bool)'
+// CHECK: | `-ImplicitCastExpr {{.*}} <col:38, col:45> '_Bool' <IntegralToBoolean>
+// CHECK: |   `-BinaryOperator {{.*}} <col:38, col:45> 'int' '>='
+// CHECK: |     |-UnaryOperator {{.*}} <col:38, col:40> 'int' prefix '++'
+// CHECK: |     | `-DeclRefExpr {{.*}} <col:40> 'int' lvalue Var {{.*}} 'x' 'int'
+// CHECK: |     `-IntegerLiteral {{.*}} <col:45> 'int' 1
diff --git a/clang/test/CodeGen/builtin-static-analysis-assume.c b/clang/test/CodeGen/builtin-static-analysis-assume.c
new file mode 100644
index 0000000000000..e73c2371be3b9
--- /dev/null
+++ b/clang/test/CodeGen/builtin-static-analysis-assume.c
@@ -0,0 +1,17 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s \
+// RUN: | FileCheck --check-prefix CHECK %s
+
+// CHECK-LABEL: define dso_local i32 @fun(
+// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[X:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 0, ptr [[X]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[X]], align 4
+// CHECK-NEXT:    ret i32 [[TMP0]]
+//
+int fun() {
+    int x = 0;
+    __builtin_static_analysis_assume(++x >= 1);
+    return x;
+}
diff --git a/clang/test/Sema/builtin-static-analysis-assume.cpp b/clang/test/Sema/builtin-static-analysis-assume.cpp
new file mode 100644
index 0000000000000..3293e2ab15c55
--- /dev/null
+++ b/clang/test/Sema/builtin-static-analysis-assume.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 %s -fsyntax-only -verify -pedantic
+
+void voidfn();
+
+class Foo{};
+
+int fun() {
+    int x = 0;
+    __builtin_static_analysis_assume(true);
+    __builtin_static_analysis_assume(x <= 0);
+    __builtin_static_analysis_assume(voidfn());  // expected-error{{cannot initialize a parameter of type 'bool' with an rvalue of type 'void}}
+    __builtin_static_analysis_assume(Foo());  // expected-error{{no viable conversion from 'Foo' to 'bool'}}
+    return x;
+}



More information about the cfe-commits mailing list