CppInterOp
C++ Language Interoperability Layer
Loading...
Searching...
No Matches
CppInterOpTypes.h
Go to the documentation of this file.
1//===--- CppInterOpTypes.h - Types for CppInterOp API -----------*- C++ -*-===//
2//
3// Part of the compiler-research project, under the Apache License v2.0 with
4// LLVM Exceptions.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Opaque types, enums, and helper classes used by the CppInterOp API.
10// This header is self-contained and does not declare any API functions,
11// making it safe to include from both CppInterOp.h and Dispatch.h.
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef CPPINTEROP_CPPINTEROPTYPES_H
16#define CPPINTEROP_CPPINTEROPTYPES_H
17
18// The cross-platform CPPINTEROP_API macro definition
19#if defined _WIN32 || defined __CYGWIN__
20#define CPPINTEROP_API __declspec(dllexport)
21#else
22#ifdef __GNUC__
23#define CPPINTEROP_API __attribute__((__visibility__("default")))
24#else
25#define CPPINTEROP_API
26#endif
27#endif
28
29// Cross-platform deprecation attribute. Older Clang versions (Cling)
30// mis-parse C++11 `[[deprecated]]` near the return type, so we use
31// the vendor-specific spelling on each compiler.
32#if defined(_MSC_VER)
33#define CPPINTEROP_DEPRECATED(msg) __declspec(deprecated(msg))
34#elif defined(__GNUC__) || defined(__clang__)
35#define CPPINTEROP_DEPRECATED(msg) __attribute__((deprecated(msg)))
36#else
37#define CPPINTEROP_DEPRECATED(msg)
38#endif
39
40// C-compatible headers — usable from both C and C++.
41#include <stdbool.h>
42#include <stddef.h>
43#include <stdint.h>
44
45/// Opaque handle types for the CppInterOp C and C++ API.
46///
47/// Each handle wraps a single void* and is a distinct type so the
48/// compiler rejects cross-kind misuse (e.g. passing a TypeRef where a
49/// DeclRef is expected). Layout is identical across all handles — a
50/// single pointer — so they are trivially copyable and ABI-stable.
51///
52/// The C and C++ views are declared independently:
53/// * C sees `struct CppDeclRef { void* data; }` etc. at global scope,
54/// prefixed so they don't collide with generic names from other
55/// C libraries.
56/// * C++ sees `Cpp::DeclRef` etc. in namespace `Cpp`, no prefix —
57/// the namespace already disambiguates.
58/// Both have identical layout, so a C-linkage function exchanging these
59/// structs across the boundary is byte-identical regardless of which
60/// view names it. The TableGen-emitted C-API .inc adds C++ typedef
61/// aliases (`using CppDeclRef = Cpp::DeclRef;`) so the generated C
62/// wrappers compile under C++ using the prefixed C-side names.
63
64#ifndef __cplusplus
65
66typedef struct CppDeclRef {
67 void* data;
69typedef struct CppTypeRef {
70 void* data;
72typedef struct CppFuncRef {
73 void* data;
75typedef struct CppObjectRef {
76 void* data;
78typedef struct CppInterpRef {
79 void* data;
81typedef struct CppConstDeclRef {
82 const void* data;
84typedef struct CppConstTypeRef {
85 const void* data;
87typedef struct CppConstFuncRef {
88 const void* data;
90
91#else // __cplusplus
92
93namespace Cpp {
94
95struct DeclRef {
96 void* data;
97 DeclRef() : data(nullptr) {}
98 DeclRef(void* P) : data(P) {}
99 DeclRef(decltype(nullptr)) : data(nullptr) {}
100 explicit operator bool() const { return data != nullptr; }
101 friend bool operator==(DeclRef a, DeclRef b) { return a.data == b.data; }
102 friend bool operator!=(DeclRef a, DeclRef b) { return !(a == b); }
103};
104
105struct TypeRef {
106 void* data;
107 TypeRef() : data(nullptr) {}
108 TypeRef(void* P) : data(P) {}
109 TypeRef(decltype(nullptr)) : data(nullptr) {}
110 explicit operator bool() const { return data != nullptr; }
111 friend bool operator==(TypeRef a, TypeRef b) { return a.data == b.data; }
112 friend bool operator!=(TypeRef a, TypeRef b) { return !(a == b); }
113};
114
115struct FuncRef {
116 void* data;
117 FuncRef() : data(nullptr) {}
118 FuncRef(void* P) : data(P) {}
119 FuncRef(decltype(nullptr)) : data(nullptr) {}
120 explicit operator bool() const { return data != nullptr; }
121 friend bool operator==(FuncRef a, FuncRef b) { return a.data == b.data; }
122 friend bool operator!=(FuncRef a, FuncRef b) { return !(a == b); }
123};
124
125struct ObjectRef {
126 void* data;
127 ObjectRef() : data(nullptr) {}
128 ObjectRef(void* P) : data(P) {}
129 ObjectRef(decltype(nullptr)) : data(nullptr) {}
130 explicit operator bool() const { return data != nullptr; }
131 friend bool operator==(ObjectRef a, ObjectRef b) { return a.data == b.data; }
132 friend bool operator!=(ObjectRef a, ObjectRef b) { return !(a == b); }
133};
134
135struct InterpRef {
136 void* data;
137 InterpRef() : data(nullptr) {}
138 InterpRef(void* P) : data(P) {}
139 InterpRef(decltype(nullptr)) : data(nullptr) {}
140 explicit operator bool() const { return data != nullptr; }
141 friend bool operator==(InterpRef a, InterpRef b) { return a.data == b.data; }
142 friend bool operator!=(InterpRef a, InterpRef b) { return !(a == b); }
143};
144
145/// Const handle variants — read-only access to the underlying AST node.
146/// Implicit widening from mutable to const is allowed; narrowing is not.
147
148struct ConstDeclRef {
149 const void* data;
150 ConstDeclRef() : data(nullptr) {}
151 ConstDeclRef(const void* P) : data(P) {}
152 ConstDeclRef(DeclRef d) : data(d.data) {}
153 ConstDeclRef(decltype(nullptr)) : data(nullptr) {}
154 explicit operator bool() const { return data != nullptr; }
155 friend bool operator==(ConstDeclRef a, ConstDeclRef b) {
156 return a.data == b.data;
157 }
158 friend bool operator!=(ConstDeclRef a, ConstDeclRef b) { return !(a == b); }
159};
160
161struct ConstTypeRef {
162 const void* data;
163 ConstTypeRef() : data(nullptr) {}
164 ConstTypeRef(const void* P) : data(P) {}
165 ConstTypeRef(TypeRef d) : data(d.data) {}
166 ConstTypeRef(decltype(nullptr)) : data(nullptr) {}
167 explicit operator bool() const { return data != nullptr; }
168 friend bool operator==(ConstTypeRef a, ConstTypeRef b) {
169 return a.data == b.data;
170 }
171 friend bool operator!=(ConstTypeRef a, ConstTypeRef b) { return !(a == b); }
172};
173
174struct ConstFuncRef {
175 const void* data;
176 ConstFuncRef() : data(nullptr) {}
177 ConstFuncRef(const void* P) : data(P) {}
178 ConstFuncRef(FuncRef d) : data(d.data) {}
179 ConstFuncRef(decltype(nullptr)) : data(nullptr) {}
180 explicit operator bool() const { return data != nullptr; }
181 friend bool operator==(ConstFuncRef a, ConstFuncRef b) {
182 return a.data == b.data;
183 }
184 friend bool operator!=(ConstFuncRef a, ConstFuncRef b) { return !(a == b); }
185};
186
187} // namespace Cpp
188
189#endif // __cplusplus
190
191#ifdef __cplusplus
192#include <cassert>
193#include <cstddef>
194#include <cstdint>
195#include <set>
196#include <string>
197#include <vector>
198
199template <> struct std::hash<Cpp::DeclRef> {
200 std::size_t operator()(const Cpp::DeclRef& obj) const {
201 return std::hash<void*>{}(obj.data);
202 }
203};
204template <> struct std::hash<Cpp::TypeRef> {
205 std::size_t operator()(const Cpp::TypeRef& obj) const {
206 return std::hash<void*>{}(obj.data);
207 }
208};
209template <> struct std::hash<Cpp::FuncRef> {
210 std::size_t operator()(const Cpp::FuncRef& obj) const {
211 return std::hash<void*>{}(obj.data);
212 }
213};
214template <> struct std::hash<Cpp::ObjectRef> {
215 std::size_t operator()(const Cpp::ObjectRef& obj) const {
216 return std::hash<void*>{}(obj.data);
217 }
218};
219
220namespace Cpp {
221class JitCall;
222}
223namespace CppInternal {
224namespace DispatchRaw {
225// Trace-hook slot forward decls; the X-macro expansion of
226// CppInterOpAPI.inc re-declares these with identical types. They're
227// here so JitCall::Invoke's inline body below can reference them.
229 const Cpp::JitCall* JC, void* result, void** args, std::size_t nargs,
230 void* self);
232 const Cpp::JitCall* JC, void* object, unsigned long nary, int withFree);
234 const Cpp::JitCall* JC, void* result);
235} // namespace DispatchRaw
236} // namespace CppInternal
237
238#endif // __cplusplus
239
240/// C-compatible array of opaque pointers, returned by generated C API
241/// wrappers for functions that produce collections. The caller must free
242/// the array by calling cppinterop_DisposeArray().
243typedef struct CppInterOpArray {
244 void** data;
245 size_t size;
247
248/// C-compatible array of strings, returned by generated C API wrappers
249/// for functions that produce string collections. Each string is
250/// individually allocated with strdup(). The caller must free the array
251/// by calling cppinterop_DisposeStringArray().
256
257/// Holds information for instantiating a template.
258/// Standard-layout, C-compatible.
259typedef struct TemplateArgInfo {
260 void* m_Type;
261 const char* m_IntegralValue;
262#ifdef __cplusplus
263 TemplateArgInfo(void* type, const char* integral_value = nullptr)
264 : m_Type(type), m_IntegralValue(integral_value) {}
265#endif
267
268#ifdef __cplusplus
269namespace Cpp {
270
271// Pull file-scope C-compatible structs into the namespace.
272// The handle types (DeclRef etc.) are already defined in this namespace
273// above; only the genuinely-C-visible structs need pulling in.
274using ::CppInterOpArray;
275using ::CppInterOpStringArray;
276using ::TemplateArgInfo;
277
278static_assert(sizeof(DeclRef) == sizeof(ConstDeclRef),
279 "Const/mutable handle ABI mismatch");
280static_assert(sizeof(TypeRef) == sizeof(ConstTypeRef),
281 "Const/mutable handle ABI mismatch");
282static_assert(sizeof(FuncRef) == sizeof(ConstFuncRef),
283 "Const/mutable handle ABI mismatch");
284
285enum Operator : unsigned char {
286 OP_None,
287 OP_New,
288 OP_Delete,
289 OP_Array_New,
290 OP_Array_Delete,
291 OP_Plus,
292 OP_Minus,
293 OP_Star,
294 OP_Slash,
295 OP_Percent,
296 OP_Caret,
297 OP_Amp,
298 OP_Pipe,
299 OP_Tilde,
300 OP_Exclaim,
301 OP_Equal,
302 OP_Less,
303 OP_Greater,
304 OP_PlusEqual,
305 OP_MinusEqual,
306 OP_StarEqual,
307 OP_SlashEqual,
308 OP_PercentEqual,
309 OP_CaretEqual,
310 OP_AmpEqual,
311 OP_PipeEqual,
312 OP_LessLess,
313 OP_GreaterGreater,
314 OP_LessLessEqual,
315 OP_GreaterGreaterEqual,
316 OP_EqualEqual,
317 OP_ExclaimEqual,
318 OP_LessEqual,
319 OP_GreaterEqual,
320 OP_Spaceship,
321 OP_AmpAmp,
322 OP_PipePipe,
323 OP_PlusPlus,
324 OP_MinusMinus,
325 OP_Comma,
326 OP_ArrowStar,
327 OP_Arrow,
328 OP_Call,
329 OP_Subscript,
330 OP_Conditional,
331 OP_Coawait,
332};
333
334enum OperatorArity : unsigned char { kUnary = 1, kBinary, kBoth };
335enum Signedness : unsigned char { kSigned = 1, kUnsigned };
336
337/// Enum modelling CVR qualifiers.
338enum QualKind : unsigned char {
339 Const = 1 << 0,
340 Volatile = 1 << 1,
341 Restrict = 1 << 2,
342 All = Const | Volatile | Restrict
343};
344
345/// Enum modelling programming languages.
346enum class InterpreterLanguage : unsigned char {
347 Unknown,
348 Asm,
349 CIR,
350 LLVM_IR,
351 C,
352 CPlusPlus,
353 ObjC,
354 ObjCPlusPlus,
355 OpenCL,
356 OpenCLCXX,
357 CUDA,
358 HIP,
359 HLSL
360};
361
362/// Enum modelling language standards.
363enum class InterpreterLanguageStandard : unsigned char {
364 c89,
365 c94,
366 gnu89,
367 c99,
368 gnu99,
369 c11,
370 gnu11,
371 c17,
372 gnu17,
373 c23,
374 gnu23,
375 c2y,
376 gnu2y,
377 cxx98,
378 gnucxx98,
379 cxx11,
380 gnucxx11,
381 cxx14,
382 gnucxx14,
383 cxx17,
384 gnucxx17,
385 cxx20,
386 gnucxx20,
387 cxx23,
388 gnucxx23,
389 cxx26,
390 gnucxx26,
391 opencl10,
392 opencl11,
393 opencl12,
394 opencl20,
395 opencl30,
396 openclcpp10,
397 openclcpp2021,
398 hlsl,
399 hlsl2015,
400 hlsl2016,
401 hlsl2017,
402 hlsl2018,
403 hlsl2021,
404 hlsl202x,
405 hlsl202y,
406 lang_unspecified
407};
408
409enum class AllocType : unsigned char {
410 None,
411 New,
412 NewArr,
413 Malloc,
414 Unknown,
415 CustomAlloc
416};
417
418inline QualKind operator|(QualKind a, QualKind b) {
419 return static_cast<QualKind>(static_cast<unsigned char>(a) |
420 static_cast<unsigned char>(b));
421}
422
423enum class ValueKind : std::uint8_t {
424 None,
425 LValue,
426 RValue,
427};
428
429/// A class modeling function calls for functions produced by the interpreter
430/// in compiled code. It provides an information if we are calling a standard
431/// function, constructor or destructor.
432class JitCall {
433public:
434 friend CPPINTEROP_API JitCall MakeFunctionCallable(InterpRef I,
435 ConstFuncRef func);
436 enum Kind : char {
437 kUnknown = 0,
438 kGenericCall,
439 kConstructorCall,
440 kDestructorCall,
441 };
442 struct ArgList {
443 void** m_Args = nullptr;
444 size_t m_ArgSize = 0;
445 // Clang struggles with =default...
446 ArgList() {}
447 ArgList(void** Args, size_t ArgSize) : m_Args(Args), m_ArgSize(ArgSize) {}
448 };
449 // FIXME: Figure out how to unify the wrapper signatures.
450 // FIXME: Hide these implementation details by moving wrapper generation in
451 // this class.
452 // (self, nargs, args, result, nary)
453 using GenericCall = void (*)(void*, size_t, void**, void*);
454 // (result, nary, nargs, args, is_arena)
455 using ConstructorCall = void (*)(void*, size_t, size_t, void**, void*);
456 // (self, nary, withFree)
457 using DestructorCall = void (*)(void*, size_t, int);
458
459private:
460 union {
461 GenericCall m_GenericCall;
462 ConstructorCall m_ConstructorCall;
463 DestructorCall m_DestructorCall;
464 };
465 Kind m_Kind;
466 ConstFuncRef m_FD;
467 JitCall() : m_GenericCall(nullptr), m_Kind(kUnknown), m_FD(nullptr) {}
468 JitCall(Kind K, GenericCall C, ConstFuncRef FD)
469 : m_GenericCall(C), m_Kind(K), m_FD(FD) {}
470 JitCall(Kind K, ConstructorCall C, ConstFuncRef Ctor)
471 : m_ConstructorCall(C), m_Kind(K), m_FD(Ctor) {}
472 JitCall(Kind K, DestructorCall C, ConstFuncRef Dtor)
473 : m_DestructorCall(C), m_Kind(K), m_FD(Dtor) {}
474
475 // Trace-hook impls need private m_FD for the function-name lookup.
476 // CPPINTEROP_API matches the X-macro-generated decl in CppInterOpDecl.inc;
477 // MSVC treats a friend decl without the dllimport/dllexport attribute as
478 // a different-linkage redeclaration.
479 friend CPPINTEROP_API void
480 CppInterOpTraceJitCallInvokeImpl(const JitCall*, void* result, void** args,
481 std::size_t nargs, void* self);
482 friend CPPINTEROP_API void
483 CppInterOpTraceJitCallInvokeDestructorImpl(const JitCall*, void* object,
484 unsigned long nary, int withFree);
485 friend CPPINTEROP_API void
486 CppInterOpTraceJitCallInvokeReturnImpl(const JitCall*, void* result);
487
488 /// Checks if the passed arguments are valid for the given function.
489 CPPINTEROP_API bool AreArgumentsValid(void* result, ArgList args, void* self,
490 size_t nary) const;
491
492public:
493 [[nodiscard]] Kind getKind() const { return m_Kind; }
494 bool isValid() const { return getKind() != kUnknown; }
495 bool isInvalid() const { return !isValid(); }
496 explicit operator bool() const { return isValid(); }
497
498 // Specialized for calling void functions.
499 void Invoke(ArgList args = {}, void* self = nullptr) const {
500 Invoke(/*result=*/nullptr, args, self);
501 }
502
503 /// Makes a call to a generic function or method.
504 ///\param[in] result - the location where the return result will be placed.
505 ///\param[in] args - a pointer to a argument list and argument size.
506 ///\param[in] self - the 'this pointer' of the object.
507 // FIXME: Adjust the arguments and their types: args_size can be unsigned;
508 // self can go in the end and be nullptr by default; result can be a nullptr
509 // by default. These changes should be synchronized with the wrapper if we
510 // decide to directly.
511 void Invoke(void* result, ArgList args = {}, void* self = nullptr) const {
512 // NOLINTBEGIN(*-type-union-access)
513 // Its possible the JitCall object deals with structor decls but went
514 // through Invoke
515
516 switch (m_Kind) {
517 case kUnknown:
518 assert(0 && "Attempted to call an invalid function declaration");
519 break;
520
521 case kGenericCall:
522 // We pass 1UL to nary which is only relevant for structors
523 assert(AreArgumentsValid(result, args, self, 1UL) && "Invalid args!");
524 if (auto fn =
526 fn(this, result, args.m_Args, args.m_ArgSize, self);
527 m_GenericCall(self, args.m_ArgSize, args.m_Args, result);
528 if (auto fn = ::CppInternal::DispatchRaw::
530 fn(this, result);
531 break;
532
533 case kConstructorCall:
534 // Forward if we intended to call a constructor (nary cannot be inferred,
535 // so we stick to constructing a single object)
536 InvokeConstructor(result, /*nary=*/1UL, args, self);
537 break;
538 case kDestructorCall:
539 // Forward if we intended to call a dtor with only 1 parameter.
540 assert(!args.m_Args && "Destructor called with arguments");
541 InvokeDestructor(result, /*nary=*/0UL, /*withFree=*/true);
542 break;
543 }
544 // NOLINTEND(*-type-union-access)
545 }
546 /// Makes a call to a destructor.
547 ///\param[in] object - the pointer of the object whose destructor we call.
548 ///\param[in] nary - the count of the objects we destruct if we deal with an
549 /// array of objects.
550 ///\param[in] withFree - true if we should call operator delete or false if
551 /// we should call only the destructor.
552 // FIXME: Change the type of withFree from int to bool in the wrapper code.
553 void InvokeDestructor(void* object, unsigned long nary = 0,
554 int withFree = true) const {
555 assert(m_Kind == kDestructorCall && "Wrong overload!");
556 if (auto fn = ::CppInternal::DispatchRaw::
558 fn(this, object, nary, withFree);
559 m_DestructorCall(object, nary, withFree);
560 }
561
562 /// Makes a call to a constructor.
563 ///\param[in] result - the memory address at which we construct the object
564 /// (placement new).
565 ///\param[in] nary - Use array new if we have to construct an array of
566 /// objects (nary > 1).
567 ///\param[in] args - a pointer to a argument list and argument size.
568 ///\param[in] is_arena - a pointer that indicates if placement new is to be
569 /// used
570 // FIXME: Change the type of withFree from int to bool in the wrapper code.
571 void InvokeConstructor(void* result, unsigned long nary = 1,
572 ArgList args = {}, void* is_arena = nullptr) const {
573 assert(m_Kind == kConstructorCall && "Wrong overload!");
574 assert(AreArgumentsValid(result, args, /*self=*/nullptr, nary) &&
575 "Invalid args!");
577 fn(this, result, args.m_Args, args.m_ArgSize, nullptr);
578 m_ConstructorCall(result, nary, args.m_ArgSize, args.m_Args, is_arena);
579 if (auto fn =
581 fn(this, result);
582 }
583};
584
585/// Opaque handle returned by MakeVTableOverlay. Owns a writable copy
586/// of an instance's vtable with selected slots replaced, and the
587/// original vptr so the overlay can be undone. Itanium ABI, single
588/// inheritance: the offset-to-top + type_info prefix and all
589/// non-overlaid slots are copied verbatim from the original vtable.
590struct VTableOverlay;
591
592// Cleanup callback fired by VTableOverlay's destructor hook (see
593// MakeVTableOverlay). Receives the original instance pointer (possibly
594// dangling -- do not dereference) and the cleanup_data passed at install.
595using VTableOverlayDtorHook = void (*)(void* inst, void* cleanup_data);
596
597// FIXME: Rework GetDimensions to make this enum redundant.
598namespace DimensionValue {
599enum : long int {
600 UNKNOWN_SIZE = -1,
601};
602} // namespace DimensionValue
603
604/// @name Stream Redirection
605///@{
606
607enum CaptureStreamKind : char {
608 kStdOut = 1, ///< stdout
609 kStdErr, ///< stderr
610};
611
612///@}
613
614} // namespace Cpp
615
616#endif // __cplusplus
617#endif // CPPINTEROP_CPPINTEROPTYPES_H
#define CPPINTEROP_API
std::optional< AllocType > result
void(* CppInterOpTraceJitCallInvokeImpl)(const Cpp::JitCall *, void *, void **, std::size_t, void *)
Definition Tracing.cpp:32
void(* CppInterOpTraceJitCallInvokeReturnImpl)(const Cpp::JitCall *, void *)
Definition Tracing.cpp:38
void(* CppInterOpTraceJitCallInvokeDestructorImpl)(const Cpp::JitCall *, void *, unsigned long, int)
Definition Tracing.cpp:36
Definition Box.h:70
JitCall MakeFunctionCallable(InterpRef I, ConstFuncRef func)
const void * data
const void * data
const void * data
Opaque handle types for the CppInterOp C and C++ API.
C-compatible array of opaque pointers, returned by generated C API wrappers for functions that produc...
C-compatible array of strings, returned by generated C API wrappers for functions that produce string...
Holds information for instantiating a template.
const char * m_IntegralValue