Skip to content

[clang] Implement declcall (P2825) as a SYCL-gated extension - #22924

Draft
koparasy wants to merge 30 commits into
intel:syclfrom
koparasy:declcall-port
Draft

[clang] Implement declcall (P2825) as a SYCL-gated extension#22924
koparasy wants to merge 30 commits into
intel:syclfrom
koparasy:declcall-port

Conversation

@koparasy

Copy link
Copy Markdown
Contributor

This adds declcall, an overload-resolution hook from P2825R5. declcall(call-expression) yields a pointer, or pointer to member, to the function that overload resolution would select for the call-expression, which is an unevaluated operand.

The initial implementation is based on a prototype by Hana Dusíková (hanickadot/llvm-project); this PR ports it, fixes correctness issues, and adds test coverage.

Devirtualization. declcall of a qualified call to a virtual member produces a devirtualized pointer-to-member (dispatch skipped). This is represented by a Devirtualize bit on CXXDeclcallExpr, carried into the member-pointer APValue, and lowered via an AllowVirtual parameter in both the Itanium and Microsoft ABIs to a direct pointer rather than a vtable index. The flag is serialized (PCH/modules) and honored by both constant evaluators. Note this yields a pointer-to-member that bypasses virtual dispatch, which has no standard-C++ equivalent.

Feature gating. P2825 is not standardized. declcall is enabled as a native keyword under -fsycl; otherwise it is a Clang extension diagnosed by -Wdeclcall-extension (a warning by default, an error under -pedantic-errors). Outside those modes it remains an ordinary identifier.

Known limitations (design-level, from the paper). When the selected function relies on default arguments, or is an explicit-object-parameter ("deducing this") member function, the resulting pointer's parameter list does not match the argument list written at the call, so it cannot be invoked with those same arguments (e.g. declcall(f(1)) for void f(int, int = 1) yields void(*)(int,int)). These are limitations of the proposal itself and are documented rather than worked around.

Tests. Coverage spans Sema, SFINAE, AST dump, CodeGen (classic and -fexperimental-new-constant-interpreter), Itanium and Microsoft mangling, PCH, and C++20 modules.

hanickadot and others added 23 commits August 11, 2026 11:15
declcall (P2825R5) prototype ported from hanickadot/llvm-project onto intel/llvm sycl.

(cherry picked from commit 4c54efc218e2b81e2c014b5f328e30f6151ab191)
@koparasy
koparasy requested review from tahonermann and a lite review from Copilot August 11, 2026 20:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements declcall(call-expression) (P2825R5) in Clang as a SYCL-gated keyword/extension, introducing a new AST node (CXXDeclcallExpr) that resolves a call-expression’s overload and yields the selected function (or member function) pointer, including support for “devirtualized” qualified virtual member calls.

Changes:

  • Adds parsing, Sema, AST, serialization, ASTMatchers, and tooling support for CXXDeclcallExpr, including extension diagnostics and keyword gating.
  • Extends constant evaluation / interpreter and CodeGen to carry and lower a “devirtualized member pointer” flag.
  • Adds broad test coverage across Sema/SFINAE, AST dump, mangling, CodeGen, PCH, and C++20 modules.

Reviewed changes

Copilot reviewed 65 out of 65 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
clang/tools/libclang/CXCursor.cpp Map CXXDeclcallExpr to libclang cursor kind.
clang/test/SemaCXX/declcall.cpp Core semantic tests for declcall.
clang/test/SemaCXX/declcall-sfinae.cpp SFINAE behavior tests.
clang/test/SemaCXX/declcall-extension.cpp Extension vs SYCL keyword gating tests.
clang/test/PCH/declcall.cpp PCH serialization test for devirtualize bit.
clang/test/CodeGenCXX/mangle-declcall.cpp Itanium mangling coverage for dependent declcall.
clang/test/CodeGenCXX/declcall.cpp CodeGen + constant-interpreter coverage.
clang/test/CodeGenCXX/declcall-modules.cpp C++20 modules serialization coverage.
clang/test/AST/ast-dump-declcall.cpp AST dump coverage for declcall + devirtualized marker.
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp Static analyzer visitor handling for declcall.
clang/lib/Serialization/ASTWriterStmt.cpp Serialize CXXDeclcallExpr (+ devirtualize flag).
clang/lib/Serialization/ASTReaderStmt.cpp Deserialize CXXDeclcallExpr (+ devirtualize flag).
clang/lib/Sema/TreeTransform.h Template transformation support for CXXDeclcallExpr.
clang/lib/Sema/SemaFixItUtils.cpp Parentheses heuristics updated for CXXDeclcallExpr.
clang/lib/Sema/SemaExprCXX.cpp Sema build/diagnose logic for declcall.
clang/lib/Sema/SemaExpr.cpp Address-of member-function operand checking touched.
clang/lib/Sema/SemaExceptionSpec.cpp canThrow classification includes declcall.
clang/lib/Parse/ParseExpr.cpp Parser support for declcall(...).
clang/lib/Edit/RewriteObjCFoundationAPI.cpp Parentheses heuristics updated for CXXDeclcallExpr.
clang/lib/CodeGen/MicrosoftCXXABI.cpp ABI interface extended with AllowVirtual knobs.
clang/lib/CodeGen/ItaniumCXXABI.cpp Itanium member-pointer emission honors AllowVirtual.
clang/lib/CodeGen/CGExprScalar.cpp Scalar emission support for CXXDeclcallExpr.
clang/lib/CodeGen/CGExprConstant.cpp Member-pointer constant emission passes AllowVirtual.
clang/lib/CodeGen/CGCXXABI.h ABI interface extended for devirtualization control.
clang/lib/CodeGen/CGCXXABI.cpp Base ABI stubs updated for new parameters.
clang/lib/Basic/IdentifierTable.cpp Add KEYCXX26 status + compat diagnostics.
clang/lib/ASTMatchers/Dynamic/Registry.cpp Register cxxDeclcallExpr matcher.
clang/lib/ASTMatchers/ASTMatchersInternal.cpp Define internal matcher object.
clang/lib/AST/TextNodeDumper.cpp Dump member-pointer APValue “(devirtualized)” marker.
clang/lib/AST/StmtProfile.cpp Stmt profiling visitor for CXXDeclcallExpr.
clang/lib/AST/StmtPrinter.cpp Pretty-printer support for declcall(...).
clang/lib/AST/ItaniumMangle.cpp Vendor-extended mangling for declcall expressions.
clang/lib/AST/ExprConstant.cpp Constant-eval propagation for devirtualized member pointers.
clang/lib/AST/ExprClassification.cpp Expression classification includes declcall.
clang/lib/AST/Expr.cpp Side-effect classification includes declcall.
clang/lib/AST/ComputeDependence.cpp Dependence computation for CXXDeclcallExpr.
clang/lib/AST/ByteCode/Opcodes.td New opcode for member-pointer devirtualization.
clang/lib/AST/ByteCode/MemberPointer.h Interpreter member-pointer tracks devirtualized bit.
clang/lib/AST/ByteCode/MemberPointer.cpp APValue conversion preserves devirtualized bit.
clang/lib/AST/ByteCode/Interp.h Declare interpreter opcode handler.
clang/lib/AST/ByteCode/Interp.cpp Implement devirtualize-member-pointer opcode.
clang/lib/AST/ByteCode/Compiler.h Add bytecode compiler visitor for declcall.
clang/lib/AST/ByteCode/Compiler.cpp Emit bytecode for declcall + devirtualization.
clang/lib/AST/ASTImporter.cpp Import CXXDeclcallExpr and APValue member-pointer flag.
clang/lib/AST/APValue.cpp Extend member-pointer APValue representation with flag.
clang/lib/Analysis/UnsafeBufferUsage.cpp Treat declcall as unevaluated context in traversal.
clang/include/clang/Serialization/ASTBitCodes.h Add bitcode tag for CXXDeclcallExpr.
clang/include/clang/Sema/Sema.h Add Sema entry points for declcall.
clang/include/clang/Basic/TokenKinds.h Add KEYCXX26 and update keyword masks.
clang/include/clang/Basic/TokenKinds.def Define declcall as a C++26 keyword (SYCL-gated).
clang/include/clang/Basic/StmtNodes.td Register CXXDeclcallExpr AST node.
clang/include/clang/Basic/DiagnosticSemaKinds.td Add declcall extension/error diagnostics.
clang/include/clang/Basic/DiagnosticLexKinds.td Add C++26 keyword compatibility warning.
clang/include/clang/Basic/DiagnosticGroups.td Add -Wdeclcall-extension group.
clang/include/clang/ASTMatchers/ASTMatchers.h Expose cxxDeclcallExpr matcher in public API.
clang/include/clang/AST/TextNodeDumper.h Update dumper visitor declarations.
clang/include/clang/AST/RecursiveASTVisitor.h Add traversal macro for CXXDeclcallExpr.
clang/include/clang/AST/PropertiesBase.td Add APValue member-pointer flag to properties layer.
clang/include/clang/AST/ExprCXX.h Define CXXDeclcallExpr node.
clang/include/clang/AST/EvaluatedExprVisitor.h Mark declcall as unevaluated for evaluated-expr visitor.
clang/include/clang/AST/ComputeDependence.h Declare dependence computation for declcall.
clang/include/clang/AST/APValue.h Public API for member-pointer devirtualized flag.
clang/docs/ReleaseNotes.md Document declcall support as an extension.
clang/docs/LanguageExtensions.md Add language extension documentation for declcall.
clang-tools-extra/clang-tidy/utils/Matchers.h Treat declcall as an unevaluated context matcher.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread clang/lib/Sema/SemaExpr.cpp
Comment thread clang/lib/Parse/ParseExpr.cpp Outdated
Comment thread clang/lib/CodeGen/MicrosoftCXXABI.cpp
…call

declcall now forms its pointer to member from a qualified reference to the
selected member function, so the global CheckUseOfCXXMethodAsAddressOfOperand
short-circuit is no longer needed. Restoring it re-enables the diagnostics for
improperly taking a member function's address (unqualified name, destructor,
parenthesized), which the prototype had disabled for all code.
The argument subexpressions of the call are no longer odr-used or evaluated,
matching the paper's unevaluated-operand rule (previously they were treated as
potentially-evaluated, causing spurious odr-use and diagnostics). The selected
function is odr-used separately in the declcall's own evaluation context, so it
is still instantiated when the declcall is potentially-evaluated.
EmitMemberFunctionPointer took an AllowVirtual parameter but ignored it, so a
devirtualized declcall of a qualified virtual member still emitted a vftable
thunk. Emit a direct function pointer when devirtualization is requested.
The devirtualized flag is the only state distinguishing declcall of a qualified
virtual call from an unqualified one (both rebuild the same &Class::method
operand). Without profiling it, two inline definitions differing only in
devirtualization hash identically, so a cross-module ODR mismatch is silently
merged (import-order-dependent: direct pointer vs vtable index) instead of
diagnosed.
… in the bytecode interpreter

Calling through a devirtualized declcall member pointer under
-fexperimental-new-constant-interpreter dispatched virtually, disagreeing with
the classic evaluator and runtime codegen. The devirtualized flag is now:
preserved across MemberPointer instance-binding/path copies, restored when a
member pointer is materialized from an APValue, and honored by a new
CallMemberPtr opcode that calls directly instead of dispatching.
CXXDeclcallExpr rewrote its operand to the resolved callee, so -ast-print
emitted declcall(f) / declcall(&B::g), which does not re-parse. Store the
original call expression separately and print it, restoring round-tripping;
evaluation and codegen continue to use the resolved operand.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants