[clang] [Sema] Set __builtin_wasm placeholder type to v* instead of i (PR #181146)

Jameson Nash via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 12 06:17:25 PST 2026


https://github.com/vtjnash created https://github.com/llvm/llvm-project/pull/181146

The `rewriteBuiltinFunctionDecl` was unconditionally rewriting BuiltinFnTy to FunctionTy, by taking the type from the FDecl instead of copying it from the Fn. That confused BuildResolvedCallExpr immediately afterward, which sets `ResultTy = Context.BoolTy` if the type is declared to be Function rather than BuiltinFn. Thus this only usually matters when also using custom type checking, since otherwise this eventually extracts the type from the FuncT and overwrites the type extracted from the builtin, but that ability is disabled for builtins with custom type checking marked. Either change here is sufficient alone for the current __builtin_wasm declarations, but doing both changes seemed more conservative for possible future builtins.

This was introduced originally in
b919c7d9eb8598b7f631c1edcd0b874bbdaf99d6.

Analysis assisted by Claude Sonnet 4.5

>From d26bac25af6996c1ee8b7f5c35d399b580faef24 Mon Sep 17 00:00:00 2001
From: Jameson Nash <vtjnash at gmail.com>
Date: Thu, 12 Feb 2026 13:53:48 +0000
Subject: [PATCH] [Sema] Set __builtin_wasm placeholder type to v* instead of i

The `rewriteBuiltinFunctionDecl` was unconditionally rewriting
BuiltinFnTy to FunctionTy, by taking the type from the FDecl instead of
copying it from the Fn. That confused BuildResolvedCallExpr immediately
afterward, which sets `ResultTy = Context.BoolTy` if the type is
declared to be Function rather than BuiltinFn. Thus this only usually
matters when also using custom type checking, since otherwise this
eventually extracts the type from the FuncT and overwrites the type
extracted from the builtin, but that ability is disabled for builtins
with custom type checking marked. Either change here is sufficient alone
for the current __builtin_wasm declarations, but doing both changes
seemed more conservative for possible future builtins.

This was introduced originally in
b919c7d9eb8598b7f631c1edcd0b874bbdaf99d6.

Analysis assisted by Claude Sonnet 4.5
---
 .../clang/Basic/BuiltinsWebAssembly.def       | 25 +++++++++++--------
 clang/lib/Sema/SemaExpr.cpp                   | 20 ++++++++-------
 2 files changed, 25 insertions(+), 20 deletions(-)

diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def b/clang/include/clang/Basic/BuiltinsWebAssembly.def
index d31b72696ff4e..21527a696ba45 100644
--- a/clang/include/clang/Basic/BuiltinsWebAssembly.def
+++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def
@@ -188,16 +188,19 @@ TARGET_BUILTIN(__builtin_wasm_extract_lane_f16x8, "fV8hIi", "nc", "fp16")
 TARGET_BUILTIN(__builtin_wasm_replace_lane_f16x8, "V8hV8hIif", "nc", "fp16")
 
 // Reference Types builtins
-// Some builtins are custom type-checked - see 't' as part of the third argument,
-// in which case the argument spec (second argument) is unused.
+// Some builtin are custom type-checked (see 't' as part of the third),
+// because we cannot currently specify the custom type required here (essentially a templated
+// union of funcref_t* and externref_t in the __externref addrspace),
+// in which case only the return type is used and the pointers are `void*`.
+// The `rewriteBuiltinFunctionDecl` will adjust any addrspace 0 pointers to the argument address space.
 
-TARGET_BUILTIN(__builtin_wasm_ref_null_extern, "i", "nct", "reference-types")
-TARGET_BUILTIN(__builtin_wasm_ref_is_null_extern, "ii", "nct", "reference-types")
+TARGET_BUILTIN(__builtin_wasm_ref_null_extern, "v*1", "nct", "reference-types")
+TARGET_BUILTIN(__builtin_wasm_ref_is_null_extern, "iv*1", "nct", "reference-types")
 
 // A funcref represented as a function pointer with the funcref attribute
 // attached to the type, therefore SemaChecking will check for the right
 // return type.
-TARGET_BUILTIN(__builtin_wasm_ref_null_func, "i", "nct", "reference-types")
+TARGET_BUILTIN(__builtin_wasm_ref_null_func, "v*1", "ct", "reference-types")
 
 // Check if the runtime type of a function pointer matches its static type. Used
 // to avoid "function signature mismatch" traps. Takes a function pointer, uses
@@ -206,12 +209,12 @@ TARGET_BUILTIN(__builtin_wasm_ref_null_func, "i", "nct", "reference-types")
 TARGET_BUILTIN(__builtin_wasm_test_function_pointer_signature, "i.", "nct", "gc")
 
 // Table builtins
-TARGET_BUILTIN(__builtin_wasm_table_set,  "viii", "t", "reference-types")
-TARGET_BUILTIN(__builtin_wasm_table_get,  "iii", "t", "reference-types")
-TARGET_BUILTIN(__builtin_wasm_table_size, "zi", "nt", "reference-types")
-TARGET_BUILTIN(__builtin_wasm_table_grow, "iiii", "nt", "reference-types")
-TARGET_BUILTIN(__builtin_wasm_table_fill, "viiii", "t", "reference-types")
-TARGET_BUILTIN(__builtin_wasm_table_copy, "viiiii", "t", "reference-types")
+TARGET_BUILTIN(__builtin_wasm_table_set,  "vv*iv*1", "t", "reference-types")
+TARGET_BUILTIN(__builtin_wasm_table_get,  "v*1v*i", "t", "reference-types")
+TARGET_BUILTIN(__builtin_wasm_table_size, "zv*", "nt", "reference-types")
+TARGET_BUILTIN(__builtin_wasm_table_grow, "iv*v*1i", "nt", "reference-types")
+TARGET_BUILTIN(__builtin_wasm_table_fill, "vv*iv*1i", "t", "reference-types")
+TARGET_BUILTIN(__builtin_wasm_table_copy, "vv*v*iii", "t", "reference-types")
 
 #undef BUILTIN
 #undef TARGET_BUILTIN
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 233f9ff297608..2d21e261e7a7a 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6778,9 +6778,9 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
                rewriteBuiltinFunctionDecl(this, Context, FDecl, ArgExprs))) {
         NDecl = FDecl;
         Fn = DeclRefExpr::Create(
-            Context, FDecl->getQualifierLoc(), SourceLocation(), FDecl, false,
-            SourceLocation(), FDecl->getType(), Fn->getValueKind(), FDecl,
-            nullptr, DRE->isNonOdrUse());
+            Context, DRE->getQualifierLoc(), SourceLocation(), FDecl, false,
+            SourceLocation(), Fn->getType() /* BuiltinFnTy */,
+            Fn->getValueKind(), FDecl, nullptr, DRE->isNonOdrUse());
       }
     }
   } else if (auto *ME = dyn_cast<MemberExpr>(NakedFn))
@@ -6977,24 +6977,26 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
     }
   }
 
+  // Extract the return type from the builtin function pointer type.
+  QualType ResultTy;
+  if (BuiltinID)
+    ResultTy = FDecl->getCallResultType();
+  else
+    ResultTy = Context.BoolTy;
+
   // Promote the function operand.
   // We special-case function promotion here because we only allow promoting
   // builtin functions to function pointers in the callee of a call.
   ExprResult Result;
-  QualType ResultTy;
   if (BuiltinID &&
       Fn->getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn)) {
-    // Extract the return type from the (builtin) function pointer type.
     // FIXME Several builtins still have setType in
     // Sema::CheckBuiltinFunctionCall. One should review their definitions in
     // Builtins.td to ensure they are correct before removing setType calls.
     QualType FnPtrTy = Context.getPointerType(FDecl->getType());
     Result = ImpCastExprToType(Fn, FnPtrTy, CK_BuiltinFnToFnPtr).get();
-    ResultTy = FDecl->getCallResultType();
-  } else {
+  } else
     Result = CallExprUnaryConversions(Fn);
-    ResultTy = Context.BoolTy;
-  }
   if (Result.isInvalid())
     return ExprError();
   Fn = Result.get();



More information about the cfe-commits mailing list