forked from OSchip/llvm-project
Make sure we have a Add/Remove/Has function for various thing that can have attribute.
Summary: This also deprecated the get attribute function familly. Reviewers: Wallbraker, whitequark, joker.eph, echristo, rafael, jyknight Subscribers: axw, joker.eph, llvm-commits Differential Revision: http://reviews.llvm.org/D19181 llvm-svn: 272504
This commit is contained in:
parent
adc7939525
commit
5db224e1f0
|
@ -54,8 +54,12 @@ Non-comprehensive list of changes in this release
|
||||||
* The C API function LLVMGetDataLayout is deprecated
|
* The C API function LLVMGetDataLayout is deprecated
|
||||||
in favor of LLVMGetDataLayoutStr.
|
in favor of LLVMGetDataLayoutStr.
|
||||||
|
|
||||||
* The C API enum LLVMAttribute is deprecated in favor of
|
* The C API enum LLVMAttribute and associated API is deprecated in favor of
|
||||||
LLVMGetAttributeKindForName.
|
the new LLVMAttributeRef API. The deprecated functions are
|
||||||
|
LLVMAddFunctionAttr, LLVMAddTargetDependentFunctionAttr,
|
||||||
|
LLVMRemoveFunctionAttr, LLVMGetFunctionAttr, LLVMAddAttribute,
|
||||||
|
LLVMRemoveAttribute, LLVMGetAttribute, LLVMAddInstrAttribute and
|
||||||
|
LLVMRemoveInstrAttribute.
|
||||||
|
|
||||||
* ``TargetFrameLowering::eliminateCallFramePseudoInstr`` now returns an
|
* ``TargetFrameLowering::eliminateCallFramePseudoInstr`` now returns an
|
||||||
iterator to the next instruction instead of ``void``. Targets that previously
|
iterator to the next instruction instead of ``void``. Targets that previously
|
||||||
|
|
|
@ -380,6 +380,20 @@ typedef enum {
|
||||||
LLVMDSNote
|
LLVMDSNote
|
||||||
} LLVMDiagnosticSeverity;
|
} LLVMDiagnosticSeverity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute index are either LLVMAttributeReturnIndex,
|
||||||
|
* LLVMAttributeFunctionIndex or a parameter number from 1 to N.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
LLVMAttributeReturnIndex = 0U,
|
||||||
|
// ISO C restricts enumerator values to range of 'int'
|
||||||
|
// (4294967295 is too large)
|
||||||
|
// LLVMAttributeFunctionIndex = ~0U,
|
||||||
|
LLVMAttributeFunctionIndex = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef unsigned LLVMAttributeIndex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @}
|
* @}
|
||||||
*/
|
*/
|
||||||
|
@ -477,7 +491,7 @@ unsigned LLVMGetMDKindIDInContext(LLVMContextRef C, const char *Name,
|
||||||
unsigned LLVMGetMDKindID(const char *Name, unsigned SLen);
|
unsigned LLVMGetMDKindID(const char *Name, unsigned SLen);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an unique id given the name of a target independent attribute,
|
* Return an unique id given the name of a enum attribute,
|
||||||
* or 0 if no attribute by that name exists.
|
* or 0 if no attribute by that name exists.
|
||||||
*
|
*
|
||||||
* See http://llvm.org/docs/LangRef.html#parameter-attributes
|
* See http://llvm.org/docs/LangRef.html#parameter-attributes
|
||||||
|
@ -487,7 +501,48 @@ unsigned LLVMGetMDKindID(const char *Name, unsigned SLen);
|
||||||
* NB: Attribute names and/or id are subject to change without
|
* NB: Attribute names and/or id are subject to change without
|
||||||
* going through the C API deprecation cycle.
|
* going through the C API deprecation cycle.
|
||||||
*/
|
*/
|
||||||
unsigned LLVMGetAttributeKindForName(const char *Name, size_t SLen);
|
unsigned LLVMGetEnumAttributeKindForName(const char *Name, size_t SLen);
|
||||||
|
unsigned LLVMGetLastEnumAttributeKind();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an enum attribute.
|
||||||
|
*/
|
||||||
|
LLVMAttributeRef LLVMCreateEnumAttribute(LLVMContextRef C, unsigned KindID,
|
||||||
|
uint64_t Val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unique id corresponding to the enum attribute
|
||||||
|
* passed as argument.
|
||||||
|
*/
|
||||||
|
unsigned LLVMGetEnumAttributeKind(LLVMAttributeRef A);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the enum attribute's value. 0 is returned if none exists.
|
||||||
|
*/
|
||||||
|
uint64_t LLVMGetEnumAttributeValue(LLVMAttributeRef A);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a string attribute.
|
||||||
|
*/
|
||||||
|
LLVMAttributeRef LLVMCreateStringAttribute(LLVMContextRef C,
|
||||||
|
const char *K, unsigned KLength,
|
||||||
|
const char *V, unsigned VLength);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the string attribute's kind.
|
||||||
|
*/
|
||||||
|
const char *LLVMGetStringAttributeKind(LLVMAttributeRef A, unsigned *Length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the string attribute's value.
|
||||||
|
*/
|
||||||
|
const char *LLVMGetStringAttributeValue(LLVMAttributeRef A, unsigned *Length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for the different types of attributes.
|
||||||
|
*/
|
||||||
|
LLVMBool LLVMIsEnumAttribute(LLVMAttributeRef A);
|
||||||
|
LLVMBool LLVMIsStringAttribute(LLVMAttributeRef A);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @}
|
* @}
|
||||||
|
@ -1957,6 +2012,14 @@ void LLVMSetGC(LLVMValueRef Fn, const char *Name);
|
||||||
*/
|
*/
|
||||||
void LLVMAddFunctionAttr(LLVMValueRef Fn, LLVMAttribute PA);
|
void LLVMAddFunctionAttr(LLVMValueRef Fn, LLVMAttribute PA);
|
||||||
|
|
||||||
|
void LLVMAddAttributeAtIndex(LLVMValueRef F, LLVMAttributeIndex Idx,
|
||||||
|
LLVMAttributeRef A);
|
||||||
|
LLVMAttributeRef LLVMGetEnumAttributeAtIndex(LLVMValueRef F,
|
||||||
|
LLVMAttributeIndex Idx,
|
||||||
|
unsigned KindID);
|
||||||
|
void LLVMRemoveEnumAttributeAtIndex(LLVMValueRef F, LLVMAttributeIndex Idx,
|
||||||
|
unsigned KindID);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a target-dependent attribute to a function
|
* Add a target-dependent attribute to a function
|
||||||
* @see llvm::AttrBuilder::addAttribute()
|
* @see llvm::AttrBuilder::addAttribute()
|
||||||
|
|
|
@ -108,6 +108,13 @@ typedef struct LLVMOpaquePassRegistry *LLVMPassRegistryRef;
|
||||||
* @see llvm::Use */
|
* @see llvm::Use */
|
||||||
typedef struct LLVMOpaqueUse *LLVMUseRef;
|
typedef struct LLVMOpaqueUse *LLVMUseRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to represent an attributes.
|
||||||
|
*
|
||||||
|
* @see llvm::Attribute
|
||||||
|
*/
|
||||||
|
typedef struct LLVMOpaqueAttributeRef *LLVMAttributeRef;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see llvm::DiagnosticInfo
|
* @see llvm::DiagnosticInfo
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "llvm/ADT/Optional.h"
|
#include "llvm/ADT/Optional.h"
|
||||||
#include "llvm/Support/Compiler.h"
|
#include "llvm/Support/Compiler.h"
|
||||||
#include "llvm/Support/PointerLikeTypeTraits.h"
|
#include "llvm/Support/PointerLikeTypeTraits.h"
|
||||||
|
#include "llvm-c/Types.h"
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@ -169,8 +170,28 @@ public:
|
||||||
void Profile(FoldingSetNodeID &ID) const {
|
void Profile(FoldingSetNodeID &ID) const {
|
||||||
ID.AddPointer(pImpl);
|
ID.AddPointer(pImpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Return a raw pointer that uniquely identifies this attribute.
|
||||||
|
void *getRawPointer() const {
|
||||||
|
return pImpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Get an attribute from a raw pointer created by getRawPointer.
|
||||||
|
static Attribute fromRawPointer(void *RawPtr) {
|
||||||
|
return Attribute(reinterpret_cast<AttributeImpl*>(RawPtr));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Specialized opaque value conversions.
|
||||||
|
inline LLVMAttributeRef wrap(Attribute Attr) {
|
||||||
|
return reinterpret_cast<LLVMAttributeRef>(Attr.getRawPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specialized opaque value conversions.
|
||||||
|
inline Attribute unwrap(LLVMAttributeRef Attr) {
|
||||||
|
return Attribute::fromRawPointer(Attr);
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
/// \class
|
/// \class
|
||||||
/// \brief This class holds the attributes for a function, its return value, and
|
/// \brief This class holds the attributes for a function, its return value, and
|
||||||
|
|
|
@ -205,12 +205,10 @@ public:
|
||||||
|
|
||||||
/// @brief Return the attribute for the given attribute kind.
|
/// @brief Return the attribute for the given attribute kind.
|
||||||
Attribute getFnAttribute(Attribute::AttrKind Kind) const {
|
Attribute getFnAttribute(Attribute::AttrKind Kind) const {
|
||||||
if (!hasFnAttribute(Kind))
|
return getAttribute(AttributeSet::FunctionIndex, Kind);
|
||||||
return Attribute();
|
|
||||||
return AttributeSets.getAttribute(AttributeSet::FunctionIndex, Kind);
|
|
||||||
}
|
}
|
||||||
Attribute getFnAttribute(StringRef Kind) const {
|
Attribute getFnAttribute(StringRef Kind) const {
|
||||||
return AttributeSets.getAttribute(AttributeSet::FunctionIndex, Kind);
|
return getAttribute(AttributeSet::FunctionIndex, Kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Return the stack alignment for the function.
|
/// \brief Return the stack alignment for the function.
|
||||||
|
@ -232,6 +230,9 @@ public:
|
||||||
/// @brief adds the attribute to the list of attributes.
|
/// @brief adds the attribute to the list of attributes.
|
||||||
void addAttribute(unsigned i, Attribute::AttrKind attr);
|
void addAttribute(unsigned i, Attribute::AttrKind attr);
|
||||||
|
|
||||||
|
/// @brief adds the attribute to the list of attributes.
|
||||||
|
void addAttribute(unsigned i, Attribute Attr);
|
||||||
|
|
||||||
/// @brief adds the attributes to the list of attributes.
|
/// @brief adds the attributes to the list of attributes.
|
||||||
void addAttributes(unsigned i, AttributeSet attrs);
|
void addAttributes(unsigned i, AttributeSet attrs);
|
||||||
|
|
||||||
|
@ -246,6 +247,14 @@ public:
|
||||||
return getAttributes().hasAttribute(i, attr);
|
return getAttributes().hasAttribute(i, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Attribute getAttribute(unsigned i, Attribute::AttrKind Kind) const {
|
||||||
|
return AttributeSets.getAttribute(i, Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
Attribute getAttribute(unsigned i, StringRef Kind) const {
|
||||||
|
return AttributeSets.getAttribute(i, Kind);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief adds the dereferenceable attribute to the list of attributes.
|
/// @brief adds the dereferenceable attribute to the list of attributes.
|
||||||
void addDereferenceableAttr(unsigned i, uint64_t Bytes);
|
void addDereferenceableAttr(unsigned i, uint64_t Bytes);
|
||||||
|
|
||||||
|
|
|
@ -177,6 +177,9 @@ public:
|
||||||
|
|
||||||
static AttributeSetNode *get(LLVMContext &C, ArrayRef<Attribute> Attrs);
|
static AttributeSetNode *get(LLVMContext &C, ArrayRef<Attribute> Attrs);
|
||||||
|
|
||||||
|
/// \brief Return the number of attributes this AttributeSet contains.
|
||||||
|
unsigned getNumAttributes() const { return NumAttrs; }
|
||||||
|
|
||||||
bool hasAttribute(Attribute::AttrKind Kind) const {
|
bool hasAttribute(Attribute::AttrKind Kind) const {
|
||||||
return AvailableAttrs & ((uint64_t)1) << Kind;
|
return AvailableAttrs & ((uint64_t)1) << Kind;
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,10 +124,60 @@ unsigned LLVMGetMDKindID(const char *Name, unsigned SLen) {
|
||||||
#define GET_ATTR_KIND_FROM_NAME
|
#define GET_ATTR_KIND_FROM_NAME
|
||||||
#include "AttributesCompatFunc.inc"
|
#include "AttributesCompatFunc.inc"
|
||||||
|
|
||||||
unsigned LLVMGetAttributeKindForName(const char *Name, size_t SLen) {
|
unsigned LLVMGetEnumAttributeKindForName(const char *Name, size_t SLen) {
|
||||||
return getAttrKindFromName(StringRef(Name, SLen));
|
return getAttrKindFromName(StringRef(Name, SLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned LLVMGetLastEnumAttributeKind() {
|
||||||
|
return Attribute::AttrKind::EndAttrKinds;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMAttributeRef LLVMCreateEnumAttribute(LLVMContextRef C, unsigned KindID,
|
||||||
|
uint64_t Val) {
|
||||||
|
return wrap(Attribute::get(*unwrap(C), (Attribute::AttrKind)KindID, Val));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned LLVMGetEnumAttributeKind(LLVMAttributeRef A) {
|
||||||
|
return unwrap(A).getKindAsEnum();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t LLVMGetEnumAttributeValue(LLVMAttributeRef A) {
|
||||||
|
auto Attr = unwrap(A);
|
||||||
|
if (Attr.isEnumAttribute())
|
||||||
|
return 0;
|
||||||
|
return Attr.getValueAsInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMAttributeRef LLVMCreateStringAttribute(LLVMContextRef C,
|
||||||
|
const char *K, unsigned KLength,
|
||||||
|
const char *V, unsigned VLength) {
|
||||||
|
return wrap(Attribute::get(*unwrap(C), StringRef(K, KLength),
|
||||||
|
StringRef(V, VLength)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *LLVMGetStringAttributeKind(LLVMAttributeRef A,
|
||||||
|
unsigned *Length) {
|
||||||
|
auto S = unwrap(A).getKindAsString();
|
||||||
|
*Length = S.size();
|
||||||
|
return S.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *LLVMGetStringAttributeValue(LLVMAttributeRef A,
|
||||||
|
unsigned *Length) {
|
||||||
|
auto S = unwrap(A).getValueAsString();
|
||||||
|
*Length = S.size();
|
||||||
|
return S.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMBool LLVMIsEnumAttribute(LLVMAttributeRef A) {
|
||||||
|
auto Attr = unwrap(A);
|
||||||
|
return Attr.isEnumAttribute() || Attr.isIntAttribute();
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMBool LLVMIsStringAttribute(LLVMAttributeRef A) {
|
||||||
|
return unwrap(A).isStringAttribute();
|
||||||
|
}
|
||||||
|
|
||||||
char *LLVMGetDiagInfoDescription(LLVMDiagnosticInfoRef DI) {
|
char *LLVMGetDiagInfoDescription(LLVMDiagnosticInfoRef DI) {
|
||||||
std::string MsgStorage;
|
std::string MsgStorage;
|
||||||
raw_string_ostream Stream(MsgStorage);
|
raw_string_ostream Stream(MsgStorage);
|
||||||
|
@ -1789,6 +1839,23 @@ void LLVMAddFunctionAttr(LLVMValueRef Fn, LLVMAttribute PA) {
|
||||||
Func->setAttributes(PALnew);
|
Func->setAttributes(PALnew);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LLVMAddAttributeAtIndex(LLVMValueRef F, LLVMAttributeIndex Idx,
|
||||||
|
LLVMAttributeRef A) {
|
||||||
|
unwrap<Function>(F)->addAttribute(Idx, unwrap(A));
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMAttributeRef LLVMGetEnumAttributeAtIndex(LLVMValueRef F,
|
||||||
|
LLVMAttributeIndex Idx,
|
||||||
|
unsigned KindID) {
|
||||||
|
return wrap(unwrap<Function>(F)->getAttribute(Idx,
|
||||||
|
(Attribute::AttrKind)KindID));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLVMRemoveEnumAttributeAtIndex(LLVMValueRef F, LLVMAttributeIndex Idx,
|
||||||
|
unsigned KindID) {
|
||||||
|
unwrap<Function>(F)->removeAttribute(Idx, (Attribute::AttrKind)KindID);
|
||||||
|
}
|
||||||
|
|
||||||
void LLVMAddTargetDependentFunctionAttr(LLVMValueRef Fn, const char *A,
|
void LLVMAddTargetDependentFunctionAttr(LLVMValueRef Fn, const char *A,
|
||||||
const char *V) {
|
const char *V) {
|
||||||
Function *Func = unwrap<Function>(Fn);
|
Function *Func = unwrap<Function>(Fn);
|
||||||
|
@ -1894,7 +1961,6 @@ LLVMAttribute LLVMGetAttribute(LLVMValueRef Arg) {
|
||||||
Raw(A->getArgNo()+1);
|
Raw(A->getArgNo()+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LLVMSetParamAlignment(LLVMValueRef Arg, unsigned align) {
|
void LLVMSetParamAlignment(LLVMValueRef Arg, unsigned align) {
|
||||||
Argument *A = unwrap<Argument>(Arg);
|
Argument *A = unwrap<Argument>(Arg);
|
||||||
AttrBuilder B;
|
AttrBuilder B;
|
||||||
|
|
|
@ -372,6 +372,12 @@ void Function::addAttribute(unsigned i, Attribute::AttrKind attr) {
|
||||||
setAttributes(PAL);
|
setAttributes(PAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Function::addAttribute(unsigned i, Attribute Attr) {
|
||||||
|
AttributeSet PAL = getAttributes();
|
||||||
|
PAL = PAL.addAttribute(getContext(), i, Attr);
|
||||||
|
setAttributes(PAL);
|
||||||
|
}
|
||||||
|
|
||||||
void Function::addAttributes(unsigned i, AttributeSet attrs) {
|
void Function::addAttributes(unsigned i, AttributeSet attrs) {
|
||||||
AttributeSet PAL = getAttributes();
|
AttributeSet PAL = getAttributes();
|
||||||
PAL = PAL.addAttributes(getContext(), i, attrs);
|
PAL = PAL.addAttributes(getContext(), i, attrs);
|
||||||
|
|
|
@ -70,13 +70,14 @@ unwind7: ; preds = %unwind5
|
||||||
|
|
||||||
declare void @_D6object6Object6__ctorFMC6object6ObjectZv(%C6object6Object*)
|
declare void @_D6object6Object6__ctorFMC6object6ObjectZv(%C6object6Object*)
|
||||||
|
|
||||||
declare i8* @_d_allocmemory(i64)
|
declare noalias i8* @_d_allocmemory(i64)
|
||||||
|
|
||||||
declare i32 @__sd_eh_personality(i32, i32, i64, i8*, i8*)
|
declare i32 @__sd_eh_personality(i32, i32, i64, i8*, i8*)
|
||||||
|
|
||||||
declare void @__sd_eh_throw(%C6object9Throwable*)
|
declare void @__sd_eh_throw(%C6object9Throwable* nonnull) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind readnone
|
; Function Attrs: nounwind readnone
|
||||||
declare i32 @llvm.eh.typeid.for(i8*) #0
|
declare i32 @llvm.eh.typeid.for(i8*) #1
|
||||||
|
|
||||||
attributes #0 = { nounwind readnone }
|
attributes #0 = { noreturn }
|
||||||
|
attributes #1 = { nounwind readnone }
|
|
@ -782,13 +782,28 @@ FunDecl:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Ctx = LLVMGetModuleContext(M);
|
||||||
|
|
||||||
Cur = Begin;
|
Cur = Begin;
|
||||||
Next = nullptr;
|
Next = nullptr;
|
||||||
while (true) {
|
while (true) {
|
||||||
const char *Name = LLVMGetValueName(Cur);
|
const char *Name = LLVMGetValueName(Cur);
|
||||||
if (LLVMGetNamedFunction(M, Name))
|
if (LLVMGetNamedFunction(M, Name))
|
||||||
report_fatal_error("Function already cloned");
|
report_fatal_error("Function already cloned");
|
||||||
LLVMAddFunction(M, Name, LLVMGetElementType(TypeCloner(M).Clone(Cur)));
|
auto Ty = LLVMGetElementType(TypeCloner(M).Clone(Cur));
|
||||||
|
auto F = LLVMAddFunction(M, Name, Ty);
|
||||||
|
|
||||||
|
// Copy attributes
|
||||||
|
for (int i = LLVMAttributeFunctionIndex, c = LLVMCountParams(F);
|
||||||
|
i <= c; ++i) {
|
||||||
|
for (unsigned k = 0, e = LLVMGetLastEnumAttributeKind(); k < e; ++k) {
|
||||||
|
if (auto SrcA = LLVMGetEnumAttributeAtIndex(Cur, i, k)) {
|
||||||
|
auto Val = LLVMGetEnumAttributeValue(SrcA);
|
||||||
|
auto DstA = LLVMCreateEnumAttribute(Ctx, k, Val);
|
||||||
|
LLVMAddAttributeAtIndex(F, i, DstA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Next = LLVMGetNextFunction(Cur);
|
Next = LLVMGetNextFunction(Cur);
|
||||||
if (Next == nullptr) {
|
if (Next == nullptr) {
|
||||||
|
|
Loading…
Reference in New Issue