1//===------------------------- MemberPointer.cpp ----------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "MemberPointer.h"
10#include "Context.h"
11#include "FunctionPointer.h"
12#include "Program.h"
13#include "Record.h"
14
15namespace clang {
16namespace interp {
17
18std::optional<Pointer> MemberPointer::toPointer(const Context &Ctx) const {
19 if (!getDecl() || isa<FunctionDecl>(Val: getDecl()))
20 return Base;
21 assert((isa<FieldDecl, IndirectFieldDecl>(getDecl())));
22
23 if (!Base.isBlockPointer())
24 return std::nullopt;
25
26 unsigned BlockMDSize = Base.block()->getDescriptor()->getMetadataSize();
27
28 if (PtrOffset >= 0) {
29 // If the resulting base would be too small, return nullopt.
30 if (Base.BS.Base < static_cast<unsigned>(PtrOffset) ||
31 (Base.BS.Base - PtrOffset < BlockMDSize))
32 return std::nullopt;
33 }
34
35 Pointer CastedBase =
36 (PtrOffset < 0 ? Base.atField(Off: -PtrOffset) : Base.atFieldSub(Off: PtrOffset));
37
38 const Record *BaseRecord = CastedBase.getRecord();
39 if (!BaseRecord)
40 return std::nullopt;
41
42 unsigned Offset = 0;
43 Offset += BlockMDSize;
44
45 if (const auto *FD = dyn_cast<FieldDecl>(Val: getDecl())) {
46 if (FD->getParent() == BaseRecord->getDecl())
47 return CastedBase.atField(Off: BaseRecord->getField(FD)->Offset);
48
49 const RecordDecl *FieldParent = FD->getParent();
50 const Record *FieldRecord = Ctx.getRecord(D: FieldParent);
51
52 Offset += FieldRecord->getField(FD)->Offset;
53 if (Offset > CastedBase.block()->getSize())
54 return std::nullopt;
55
56 if (const RecordDecl *BaseDecl = Base.getDeclPtr().getRecord()->getDecl();
57 BaseDecl != FieldParent)
58 Offset += Ctx.collectBaseOffset(BaseDecl: FieldParent, DerivedDecl: BaseDecl);
59
60 } else {
61 const auto *IFD = cast<IndirectFieldDecl>(Val: getDecl());
62
63 for (const NamedDecl *ND : IFD->chain()) {
64 const FieldDecl *F = cast<FieldDecl>(Val: ND);
65 const RecordDecl *FieldParent = F->getParent();
66 const Record *FieldRecord = Ctx.getRecord(D: FieldParent);
67 Offset += FieldRecord->getField(FD: F)->Offset;
68 }
69 }
70
71 assert(BaseRecord);
72 if (Offset > CastedBase.block()->getSize())
73 return std::nullopt;
74
75 assert(Offset <= CastedBase.block()->getSize());
76 return Pointer(const_cast<Block *>(Base.block()), Offset, Offset);
77}
78
79FunctionPointer MemberPointer::toFunctionPointer(const Context &Ctx) const {
80 return FunctionPointer(
81 Ctx.getProgram().getFunction(F: cast<FunctionDecl>(Val: getDecl())));
82}
83
84APValue MemberPointer::toAPValue(const ASTContext &ASTCtx) const {
85 if (isZero())
86 return APValue(static_cast<ValueDecl *>(nullptr), /*IsDerivedMember=*/false,
87 /*Path=*/{});
88
89 if (hasBase())
90 return Base.toAPValue(ASTCtx);
91
92 return APValue(getDecl(), /*IsDerivedMember=*/isDerivedMember(),
93 /*Path=*/ArrayRef(Path, PathLength));
94}
95
96ComparisonCategoryResult
97MemberPointer::compare(const MemberPointer &RHS) const {
98 if (this->getDecl() == RHS.getDecl()) {
99
100 if (this->PathLength != RHS.PathLength)
101 return ComparisonCategoryResult::Unordered;
102
103 if (PathLength != 0 &&
104 std::memcmp(s1: Path, s2: RHS.Path, n: PathLength * sizeof(CXXRecordDecl *)) != 0)
105 return ComparisonCategoryResult::Unordered;
106
107 return ComparisonCategoryResult::Equal;
108 }
109 return ComparisonCategoryResult::Unordered;
110}
111
112} // namespace interp
113} // namespace clang
114