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};
408inline QualKind operator|(QualKind a, QualKind b) {
409 return static_cast<QualKind>(static_cast<unsigned char>(a) |
410 static_cast<unsigned char>(b));
411}
412
413enum class ValueKind : std::uint8_t {
414 None,
415 LValue,
416 RValue,
417};
418
419/// A class modeling function calls for functions produced by the interpreter
420/// in compiled code. It provides an information if we are calling a standard
421/// function, constructor or destructor.
422class JitCall {
423public:
424 friend CPPINTEROP_API JitCall MakeFunctionCallable(InterpRef I,
425 ConstFuncRef func);
426 enum Kind : char {
427 kUnknown = 0,
428 kGenericCall,
429 kConstructorCall,
430 kDestructorCall,
431 };
432 struct ArgList {
433 void** m_Args = nullptr;
434 size_t m_ArgSize = 0;
435 // Clang struggles with =default...
436 ArgList() {}
437 ArgList(void** Args, size_t ArgSize) : m_Args(Args), m_ArgSize(ArgSize) {}
438 };
439 // FIXME: Figure out how to unify the wrapper signatures.
440 // FIXME: Hide these implementation details by moving wrapper generation in
441 // this class.
442 // (self, nargs, args, result, nary)
443 using GenericCall = void (*)(void*, size_t, void**, void*);
444 // (result, nary, nargs, args, is_arena)
445 using ConstructorCall = void (*)(void*, size_t, size_t, void**, void*);
446 // (self, nary, withFree)
447 using DestructorCall = void (*)(void*, size_t, int);
448
449private:
450 union {
451 GenericCall m_GenericCall;
452 ConstructorCall m_ConstructorCall;
453 DestructorCall m_DestructorCall;
454 };
455 Kind m_Kind;
456 ConstFuncRef m_FD;
457 JitCall() : m_GenericCall(nullptr), m_Kind(kUnknown), m_FD(nullptr) {}
458 JitCall(Kind K, GenericCall C, ConstFuncRef FD)
459 : m_GenericCall(C), m_Kind(K), m_FD(FD) {}
460 JitCall(Kind K, ConstructorCall C, ConstFuncRef Ctor)
461 : m_ConstructorCall(C), m_Kind(K), m_FD(Ctor) {}
462 JitCall(Kind K, DestructorCall C, ConstFuncRef Dtor)
463 : m_DestructorCall(C), m_Kind(K), m_FD(Dtor) {}
464
465 // Trace-hook impls need private m_FD for the function-name lookup.
466 // CPPINTEROP_API matches the X-macro-generated decl in CppInterOpDecl.inc;
467 // MSVC treats a friend decl without the dllimport/dllexport attribute as
468 // a different-linkage redeclaration.
469 friend CPPINTEROP_API void
470 CppInterOpTraceJitCallInvokeImpl(const JitCall*, void* result, void** args,
471 std::size_t nargs, void* self);
472 friend CPPINTEROP_API void
473 CppInterOpTraceJitCallInvokeDestructorImpl(const JitCall*, void* object,
474 unsigned long nary, int withFree);
475 friend CPPINTEROP_API void
476 CppInterOpTraceJitCallInvokeReturnImpl(const JitCall*, void* result);
477
478 /// Checks if the passed arguments are valid for the given function.
479 CPPINTEROP_API bool AreArgumentsValid(void* result, ArgList args, void* self,
480 size_t nary) const;
481
482public:
483 [[nodiscard]] Kind getKind() const { return m_Kind; }
484 bool isValid() const { return getKind() != kUnknown; }
485 bool isInvalid() const { return !isValid(); }
486 explicit operator bool() const { return isValid(); }
487
488 // Specialized for calling void functions.
489 void Invoke(ArgList args = {}, void* self = nullptr) const {
490 Invoke(/*result=*/nullptr, args, self);
491 }
492
493 /// Makes a call to a generic function or method.
494 ///\param[in] result - the location where the return result will be placed.
495 ///\param[in] args - a pointer to a argument list and argument size.
496 ///\param[in] self - the 'this pointer' of the object.
497 // FIXME: Adjust the arguments and their types: args_size can be unsigned;
498 // self can go in the end and be nullptr by default; result can be a nullptr
499 // by default. These changes should be synchronized with the wrapper if we
500 // decide to directly.
501 void Invoke(void* result, ArgList args = {}, void* self = nullptr) const {
502 // NOLINTBEGIN(*-type-union-access)
503 // Its possible the JitCall object deals with structor decls but went
504 // through Invoke
505
506 switch (m_Kind) {
507 case kUnknown:
508 assert(0 && "Attempted to call an invalid function declaration");
509 break;
510
511 case kGenericCall:
512 // We pass 1UL to nary which is only relevant for structors
513 assert(AreArgumentsValid(result, args, self, 1UL) && "Invalid args!");
514 if (auto fn =
516 fn(this, result, args.m_Args, args.m_ArgSize, self);
517 m_GenericCall(self, args.m_ArgSize, args.m_Args, result);
518 if (auto fn = ::CppInternal::DispatchRaw::
520 fn(this, result);
521 break;
522
523 case kConstructorCall:
524 // Forward if we intended to call a constructor (nary cannot be inferred,
525 // so we stick to constructing a single object)
526 InvokeConstructor(result, /*nary=*/1UL, args, self);
527 break;
528 case kDestructorCall:
529 // Forward if we intended to call a dtor with only 1 parameter.
530 assert(!args.m_Args && "Destructor called with arguments");
531 InvokeDestructor(result, /*nary=*/0UL, /*withFree=*/true);
532 break;
533 }
534 // NOLINTEND(*-type-union-access)
535 }
536 /// Makes a call to a destructor.
537 ///\param[in] object - the pointer of the object whose destructor we call.
538 ///\param[in] nary - the count of the objects we destruct if we deal with an
539 /// array of objects.
540 ///\param[in] withFree - true if we should call operator delete or false if
541 /// we should call only the destructor.
542 // FIXME: Change the type of withFree from int to bool in the wrapper code.
543 void InvokeDestructor(void* object, unsigned long nary = 0,
544 int withFree = true) const {
545 assert(m_Kind == kDestructorCall && "Wrong overload!");
546 if (auto fn = ::CppInternal::DispatchRaw::
548 fn(this, object, nary, withFree);
549 m_DestructorCall(object, nary, withFree);
550 }
551
552 /// Makes a call to a constructor.
553 ///\param[in] result - the memory address at which we construct the object
554 /// (placement new).
555 ///\param[in] nary - Use array new if we have to construct an array of
556 /// objects (nary > 1).
557 ///\param[in] args - a pointer to a argument list and argument size.
558 ///\param[in] is_arena - a pointer that indicates if placement new is to be
559 /// used
560 // FIXME: Change the type of withFree from int to bool in the wrapper code.
561 void InvokeConstructor(void* result, unsigned long nary = 1,
562 ArgList args = {}, void* is_arena = nullptr) const {
563 assert(m_Kind == kConstructorCall && "Wrong overload!");
564 assert(AreArgumentsValid(result, args, /*self=*/nullptr, nary) &&
565 "Invalid args!");
567 fn(this, result, args.m_Args, args.m_ArgSize, nullptr);
568 m_ConstructorCall(result, nary, args.m_ArgSize, args.m_Args, is_arena);
569 if (auto fn =
571 fn(this, result);
572 }
573};
574
575/// Opaque handle returned by MakeVTableOverlay. Owns a writable copy
576/// of an instance's vtable with selected slots replaced, and the
577/// original vptr so the overlay can be undone. Itanium ABI, single
578/// inheritance: the offset-to-top + type_info prefix and all
579/// non-overlaid slots are copied verbatim from the original vtable.
580struct VTableOverlay;
581
582// Cleanup callback fired by VTableOverlay's destructor hook (see
583// MakeVTableOverlay). Receives the original instance pointer (possibly
584// dangling -- do not dereference) and the cleanup_data passed at install.
585using VTableOverlayDtorHook = void (*)(void* inst, void* cleanup_data);
586
587// FIXME: Rework GetDimensions to make this enum redundant.
588namespace DimensionValue {
589enum : long int {
590 UNKNOWN_SIZE = -1,
591};
592} // namespace DimensionValue
593
594/// @name Stream Redirection
595///@{
596
597enum CaptureStreamKind : char {
598 kStdOut = 1, ///< stdout
599 kStdErr, ///< stderr
600};
601
602///@}
603
604} // namespace Cpp
605
606#endif // __cplusplus
607#endif // CPPINTEROP_CPPINTEROPTYPES_H
#define CPPINTEROP_API
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