https://github.com/yonghong-song created https://github.com/llvm/llvm-project/pull/235231
Sema::BuildBuiltinCallExpr() refers to the builtin with the type of its declaration, so the callee of the generated call is a reference to an ordinary function: ``` ImplicitCastExpr 'void *(*)() noexcept' <FunctionToPointerDecay> `-DeclRefExpr 'void *() noexcept' lvalue Function '__builtin_coro_frame' ``` But a builtin written in the source, which is not directly addressable, is referred to with the builtin function type and converted by BuiltinFnToFnPtr: ``` ImplicitCastExpr 'void *(*)() noexcept' <BuiltinFnToFnPtr> `-DeclRefExpr '<builtin fn type>' Function '__builtin_coro_frame' ``` The two forms are not handled the same way. For example, for a builtin which cannot be constant evaluated, the constant evaluator gives up on the BuiltinFnToFnPtr callee right away, but for the FunctionToPointerDecay callee it evaluates the arguments first, which may emit additional notes, e.g., "dereferencing a null pointer is not allowed in a constant expression". Build the reference with BuildDeclarationNameExpr() as the parser does, so a builtin call built by Sema is the same as an explicit one. This will be used by a following BPF change, which builds a __builtin_preserve_enum_value() call in Sema and expects the same diagnostics as an explicit call. The only visible change is the AST of the generated builtin calls, e.g., for coroutine builtins and HLSL __builtin_get_spirv_spec_constant_*. >From 08008a49ef34381e74144aafe7ff4738ee3ba957 Mon Sep 17 00:00:00 2001 From: Yonghong Song <[email protected]> Date: Fri, 23 Oct 2026 22:44:10 -0700 Subject: [PATCH] [clang][Sema] Build the callee in BuildBuiltinCallExpr() as in source Sema::BuildBuiltinCallExpr() refers to the builtin with the type of its declaration, so the callee of the generated call is a reference to an ordinary function: ImplicitCastExpr 'void *(*)() noexcept' <FunctionToPointerDecay> `-DeclRefExpr 'void *() noexcept' lvalue Function '__builtin_coro_frame' But a builtin written in the source, which is not directly addressable, is referred to with the builtin function type and converted by BuiltinFnToFnPtr: ImplicitCastExpr 'void *(*)() noexcept' <BuiltinFnToFnPtr> `-DeclRefExpr '<builtin fn type>' Function '__builtin_coro_frame' The two forms are not handled the same way. For example, for a builtin which cannot be constant evaluated, the constant evaluator gives up on the BuiltinFnToFnPtr callee right away, but for the FunctionToPointerDecay callee it evaluates the arguments first, which may emit additional notes, e.g., "dereferencing a null pointer is not allowed in a constant expression". Build the reference with BuildDeclarationNameExpr() as the parser does, so a builtin call built by Sema is the same as an explicit one. This will be used by a following BPF change, which builds a __builtin_preserve_enum_value() call in Sema and expects the same diagnostics as an explicit call. The only visible change is the AST of the generated builtin calls, e.g., for coroutine builtins and HLSL __builtin_get_spirv_spec_constant_*. --- clang/lib/Sema/SemaExpr.cpp | 6 ++- .../test/AST/HLSL/vk.spec-constant.usage.hlsl | 48 +++++++++---------- clang/test/SemaCXX/co_await-ast.cpp | 4 +- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 43b0c86132172b..0e8b67145aef85 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7255,8 +7255,10 @@ Expr *Sema::BuildBuiltinCallExpr(SourceLocation Loc, Builtin::ID Id, auto *BuiltInDecl = R.getAsSingle<FunctionDecl>(); assert(BuiltInDecl && "failed to find builtin declaration"); - ExprResult DeclRef = - BuildDeclRefExpr(BuiltInDecl, BuiltInDecl->getType(), VK_LValue, Loc); + // Refer to the builtin as if it were written in the source, so that the + // call is handled the same as an explicit one. + ExprResult DeclRef = BuildDeclarationNameExpr( + CXXScopeSpec(), R.getLookupNameInfo(), BuiltInDecl); assert(DeclRef.isUsable() && "Builtin reference cannot fail"); ExprResult Call = diff --git a/clang/test/AST/HLSL/vk.spec-constant.usage.hlsl b/clang/test/AST/HLSL/vk.spec-constant.usage.hlsl index a7e8223a325e67..c8b5648b3a7ca2 100644 --- a/clang/test/AST/HLSL/vk.spec-constant.usage.hlsl +++ b/clang/test/AST/HLSL/vk.spec-constant.usage.hlsl @@ -2,8 +2,8 @@ // CHECK: VarDecl {{.*}} bool_const 'const hlsl_private bool' static cinit // CHECK-NEXT: CallExpr {{.*}} 'bool' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool (*)(unsigned int, bool) noexcept' <FunctionToPointerDecay> -// CHECK-NEXT: DeclRefExpr {{.*}} 'bool (unsigned int, bool) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_bool' 'bool (unsigned int, bool) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool (*)(unsigned int, bool) noexcept' <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_get_spirv_spec_constant_bool' 'bool (unsigned int, bool) noexcept' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1 // CHECK-NEXT: CXXBoolLiteralExpr {{.*}} 'bool' true @@ -12,8 +12,8 @@ const bool bool_const = true; // CHECK: VarDecl {{.*}} short_const 'const hlsl_private short' static cinit // CHECK-NEXT: CallExpr {{.*}} 'short' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'short (*)(unsigned int, short) noexcept' <FunctionToPointerDecay> -// CHECK-NEXT: DeclRefExpr {{.*}} 'short (unsigned int, short) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_short' 'short (unsigned int, short) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'short (*)(unsigned int, short) noexcept' <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_get_spirv_spec_constant_short' 'short (unsigned int, short) noexcept' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 2 // CHECK-NEXT: ImplicitCastExpr {{.*}} 'short' <IntegralCast> @@ -23,8 +23,8 @@ const short short_const = 4; // CHECK: VarDecl {{.*}} int_const 'const hlsl_private int' static cinit // CHECK-NEXT: CallExpr {{.*}} 'int' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(unsigned int, int) noexcept' <FunctionToPointerDecay> -// CHECK-NEXT: DeclRefExpr {{.*}} 'int (unsigned int, int) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_int' 'int (unsigned int, int) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(unsigned int, int) noexcept' <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_get_spirv_spec_constant_int' 'int (unsigned int, int) noexcept' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3 // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 5 @@ -33,8 +33,8 @@ const int int_const = 5; // CHECK: VarDecl {{.*}} long_const 'const hlsl_private long long' static cinit // CHECK-NEXT: CallExpr {{.*}} 'long long' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'long long (*)(unsigned int, long long) noexcept' <FunctionToPointerDecay> -// CHECK-NEXT: DeclRefExpr {{.*}} 'long long (unsigned int, long long) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_longlong' 'long long (unsigned int, long long) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'long long (*)(unsigned int, long long) noexcept' <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_get_spirv_spec_constant_longlong' 'long long (unsigned int, long long) noexcept' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 4 // CHECK-NEXT: ImplicitCastExpr {{.*}} 'long long' <IntegralCast> @@ -44,8 +44,8 @@ const long long long_const = 8; // CHECK: VarDecl {{.*}} ushort_const 'const hlsl_private unsigned short' static cinit // CHECK-NEXT: CallExpr {{.*}} 'unsigned short' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned short (*)(unsigned int, unsigned short) noexcept' <FunctionToPointerDecay> -// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned short (unsigned int, unsigned short) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_ushort' 'unsigned short (unsigned int, unsigned short) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned short (*)(unsigned int, unsigned short) noexcept' <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_get_spirv_spec_constant_ushort' 'unsigned short (unsigned int, unsigned short) noexcept' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 5 // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned short' <IntegralCast> @@ -55,8 +55,8 @@ const unsigned short ushort_const = 10; // CHECK: VarDecl {{.*}} uint_const 'const hlsl_private unsigned int' static cinit // CHECK-NEXT: CallExpr {{.*}} 'unsigned int' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int (*)(unsigned int, unsigned int) noexcept' <FunctionToPointerDecay> -// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int (unsigned int, unsigned int) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_uint' 'unsigned int (unsigned int, unsigned int) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int (*)(unsigned int, unsigned int) noexcept' <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_get_spirv_spec_constant_uint' 'unsigned int (unsigned int, unsigned int) noexcept' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 6 // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> @@ -66,8 +66,8 @@ const unsigned int uint_const = 12; // CHECK: VarDecl {{.*}} uint_const_2 'const hlsl_private uint':'const hlsl_private unsigned int' static cinit // CHECK-NEXT: CallExpr {{.*}} 'unsigned int' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int (*)(unsigned int, unsigned int) noexcept' <FunctionToPointerDecay> -// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int (unsigned int, unsigned int) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_uint' 'unsigned int (unsigned int, unsigned int) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int (*)(unsigned int, unsigned int) noexcept' <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_get_spirv_spec_constant_uint' 'unsigned int (unsigned int, unsigned int) noexcept' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 6 // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> @@ -78,8 +78,8 @@ const uint uint_const_2 = 12; // CHECK: VarDecl {{.*}} ulong_const 'const hlsl_private unsigned long long' static cinit // CHECK-NEXT: CallExpr {{.*}} 'unsigned long long' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned long long (*)(unsigned int, unsigned long long) noexcept' <FunctionToPointerDecay> -// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned long long (unsigned int, unsigned long long) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_ulonglong' 'unsigned long long (unsigned int, unsigned long long) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned long long (*)(unsigned int, unsigned long long) noexcept' <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_get_spirv_spec_constant_ulonglong' 'unsigned long long (unsigned int, unsigned long long) noexcept' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 7 // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned long long' <IntegralCast> @@ -89,8 +89,8 @@ const unsigned long long ulong_const = 25; // CHECK: VarDecl {{.*}} half_const 'const hlsl_private half' static cinit // CHECK-NEXT: CallExpr {{.*}} 'half' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half (*)(unsigned int, half) noexcept' <FunctionToPointerDecay> -// CHECK-NEXT: DeclRefExpr {{.*}} 'half (unsigned int, half) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_half' 'half (unsigned int, half) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half (*)(unsigned int, half) noexcept' <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_get_spirv_spec_constant_half' 'half (unsigned int, half) noexcept' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 8 // CHECK-NEXT: ImplicitCastExpr {{.*}} 'half' <FloatingCast> @@ -100,8 +100,8 @@ const half half_const = 40.4; // CHECK: VarDecl {{.*}} float_const 'const hlsl_private float' static cinit // CHECK-NEXT: CallExpr {{.*}} 'float' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float (*)(unsigned int, float) noexcept' <FunctionToPointerDecay> -// CHECK-NEXT: DeclRefExpr {{.*}} 'float (unsigned int, float) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_float' 'float (unsigned int, float) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float (*)(unsigned int, float) noexcept' <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_get_spirv_spec_constant_float' 'float (unsigned int, float) noexcept' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 8 // CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <IntegralToFloating> @@ -111,8 +111,8 @@ const float float_const = 50; // CHECK: VarDecl {{.*}} double_const 'const hlsl_private double' static cinit // CHECK-NEXT: CallExpr {{.*}} 'double' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double (*)(unsigned int, double) noexcept' <FunctionToPointerDecay> -// CHECK-NEXT: DeclRefExpr {{.*}} 'double (unsigned int, double) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_double' 'double (unsigned int, double) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double (*)(unsigned int, double) noexcept' <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_get_spirv_spec_constant_double' 'double (unsigned int, double) noexcept' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 9 // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <IntegralToFloating> @@ -123,8 +123,8 @@ const double double_const = 100; // CHECK: VarDecl {{.*}} enum_const 'const hlsl_private E' static cinit // CHECK-NEXT: CStyleCastExpr {{.*}} 'E' <IntegralCast> // CHECK-NEXT: CallExpr {{.*}} 'int' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(unsigned int, int) noexcept' <FunctionToPointerDecay> -// CHECK-NEXT: DeclRefExpr {{.*}} 'int (unsigned int, int) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_int' 'int (unsigned int, int) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(unsigned int, int) noexcept' <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_get_spirv_spec_constant_int' 'int (unsigned int, int) noexcept' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 10 // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <IntegralCast> diff --git a/clang/test/SemaCXX/co_await-ast.cpp b/clang/test/SemaCXX/co_await-ast.cpp index 90683cd08d482c..8487a7bfd4f4cd 100644 --- a/clang/test/SemaCXX/co_await-ast.cpp +++ b/clang/test/SemaCXX/co_await-ast.cpp @@ -82,8 +82,8 @@ awaitable foo() { // CHECK: | | |-ImplicitCastExpr {{.*}} 'coroutine_handle<awaitable_frame> (*)(void *) noexcept' <FunctionToPointerDecay> // CHECK: | | | `-DeclRefExpr {{.*}} 'coroutine_handle<awaitable_frame> (void *) noexcept' lvalue CXXMethod {{.*}} 'from_address' 'coroutine_handle<awaitable_frame> (void *) noexcept' // CHECK: | | `-CallExpr {{.*}} 'void *' -// CHECK: | | `-ImplicitCastExpr {{.*}} 'void *(*)() noexcept' <FunctionToPointerDecay> -// CHECK: | | `-DeclRefExpr {{.*}} 'void *() noexcept' lvalue Function {{.*}} '__builtin_coro_frame' 'void *() noexcept' +// CHECK: | | `-ImplicitCastExpr {{.*}} 'void *(*)() noexcept' <BuiltinFnToFnPtr> +// CHECK: | | `-DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_coro_frame' 'void *() noexcept' // CHECK: | `-CXXMemberCallExpr {{.*}} 'void' // CHECK: | `-MemberExpr {{.*}} '<bound member function type>' .await_resume {{.*}} // CHECK: | `-ImplicitCastExpr {{.*}} 'const awaitable_frame::result_t' lvalue <NoOp> _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
