[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
Fri Feb 20 11:06:57 PST 2026


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

>From 601595a8f3de5f4aa6119d2612f68478fffb4af7 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       | 36 ++++++++++---------
 clang/lib/Sema/SemaExpr.cpp                   | 20 ++++++-----
 2 files changed, 31 insertions(+), 25 deletions(-)

diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def b/clang/include/clang/Basic/BuiltinsWebAssembly.def
index d31b72696ff4e..6de620cb22f28 100644
--- a/clang/include/clang/Basic/BuiltinsWebAssembly.def
+++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def
@@ -188,30 +188,34 @@ 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
+// argument), 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")
+// attached to the type, therefore SemaChecking will check for the right return
+// type.
+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
-// table.get to look up the pointer in __indirect_function_table and then
+// 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 table.get to look up the pointer in __indirect_function_table and then
 // ref.test to test the type.
 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 82da5dc032237..cc6af31499d30 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6876,9 +6876,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))
@@ -7075,24 +7075,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