| 1 | //===-- RecordOps.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 | // Operations on records (structs, classes, and unions). |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "clang/Analysis/FlowSensitive/RecordOps.h" |
| 14 | |
| 15 | #define DEBUG_TYPE "dataflow" |
| 16 | |
| 17 | namespace clang::dataflow { |
| 18 | |
| 19 | static void copyField(const ValueDecl &Field, StorageLocation *SrcFieldLoc, |
| 20 | StorageLocation *DstFieldLoc, RecordStorageLocation &Dst, |
| 21 | Environment &Env) { |
| 22 | assert(Field.getType()->isReferenceType() || |
| 23 | (SrcFieldLoc != nullptr && DstFieldLoc != nullptr)); |
| 24 | |
| 25 | if (Field.getType()->isRecordType()) { |
| 26 | copyRecord(Src&: cast<RecordStorageLocation>(Val&: *SrcFieldLoc), |
| 27 | Dst&: cast<RecordStorageLocation>(Val&: *DstFieldLoc), Env); |
| 28 | } else if (Field.getType()->isReferenceType()) { |
| 29 | Dst.setChild(D: Field, Loc: SrcFieldLoc); |
| 30 | } else { |
| 31 | if (Value *Val = Env.getValue(Loc: *SrcFieldLoc)) |
| 32 | Env.setValue(Loc: *DstFieldLoc, Val&: *Val); |
| 33 | else |
| 34 | Env.clearValue(Loc: *DstFieldLoc); |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | static void copySyntheticField(QualType FieldType, StorageLocation &SrcFieldLoc, |
| 39 | StorageLocation &DstFieldLoc, Environment &Env) { |
| 40 | if (FieldType->isRecordType()) { |
| 41 | copyRecord(Src&: cast<RecordStorageLocation>(Val&: SrcFieldLoc), |
| 42 | Dst&: cast<RecordStorageLocation>(Val&: DstFieldLoc), Env); |
| 43 | } else { |
| 44 | if (Value *Val = Env.getValue(Loc: SrcFieldLoc)) |
| 45 | Env.setValue(Loc: DstFieldLoc, Val&: *Val); |
| 46 | else |
| 47 | Env.clearValue(Loc: DstFieldLoc); |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst, |
| 52 | Environment &Env) { |
| 53 | auto SrcType = Src.getType().getCanonicalType().getUnqualifiedType(); |
| 54 | auto DstType = Dst.getType().getCanonicalType().getUnqualifiedType(); |
| 55 | |
| 56 | auto SrcDecl = SrcType->getAsCXXRecordDecl(); |
| 57 | auto DstDecl = DstType->getAsCXXRecordDecl(); |
| 58 | |
| 59 | [[maybe_unused]] bool compatibleTypes = |
| 60 | SrcType == DstType || |
| 61 | (SrcDecl != nullptr && DstDecl != nullptr && |
| 62 | (SrcDecl->isDerivedFrom(Base: DstDecl) || DstDecl->isDerivedFrom(Base: SrcDecl))); |
| 63 | |
| 64 | LLVM_DEBUG({ |
| 65 | if (!compatibleTypes) { |
| 66 | llvm::dbgs() << "Source type " << Src.getType() << "\n" ; |
| 67 | llvm::dbgs() << "Destination type " << Dst.getType() << "\n" ; |
| 68 | } |
| 69 | }); |
| 70 | assert(compatibleTypes); |
| 71 | |
| 72 | if (SrcType == DstType || (SrcDecl != nullptr && DstDecl != nullptr && |
| 73 | SrcDecl->isDerivedFrom(Base: DstDecl))) { |
| 74 | for (auto [Field, DstFieldLoc] : Dst.children()) |
| 75 | copyField(Field: *Field, SrcFieldLoc: Src.getChild(D: *Field), DstFieldLoc, Dst, Env); |
| 76 | for (const auto &[Name, DstFieldLoc] : Dst.synthetic_fields()) |
| 77 | copySyntheticField(FieldType: DstFieldLoc->getType(), SrcFieldLoc&: Src.getSyntheticField(Name), |
| 78 | DstFieldLoc&: *DstFieldLoc, Env); |
| 79 | } else { |
| 80 | for (auto [Field, SrcFieldLoc] : Src.children()) |
| 81 | copyField(Field: *Field, SrcFieldLoc, DstFieldLoc: Dst.getChild(D: *Field), Dst, Env); |
| 82 | for (const auto &[Name, SrcFieldLoc] : Src.synthetic_fields()) |
| 83 | copySyntheticField(FieldType: SrcFieldLoc->getType(), SrcFieldLoc&: *SrcFieldLoc, |
| 84 | DstFieldLoc&: Dst.getSyntheticField(Name), Env); |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | bool recordsEqual(const RecordStorageLocation &Loc1, const Environment &Env1, |
| 89 | const RecordStorageLocation &Loc2, const Environment &Env2) { |
| 90 | LLVM_DEBUG({ |
| 91 | if (Loc2.getType().getCanonicalType().getUnqualifiedType() != |
| 92 | Loc1.getType().getCanonicalType().getUnqualifiedType()) { |
| 93 | llvm::dbgs() << "Loc1 type " << Loc1.getType() << "\n" ; |
| 94 | llvm::dbgs() << "Loc2 type " << Loc2.getType() << "\n" ; |
| 95 | } |
| 96 | }); |
| 97 | assert(Loc2.getType().getCanonicalType().getUnqualifiedType() == |
| 98 | Loc1.getType().getCanonicalType().getUnqualifiedType()); |
| 99 | |
| 100 | for (auto [Field, FieldLoc1] : Loc1.children()) { |
| 101 | StorageLocation *FieldLoc2 = Loc2.getChild(D: *Field); |
| 102 | |
| 103 | assert(Field->getType()->isReferenceType() || |
| 104 | (FieldLoc1 != nullptr && FieldLoc2 != nullptr)); |
| 105 | |
| 106 | if (Field->getType()->isRecordType()) { |
| 107 | if (!recordsEqual(Loc1: cast<RecordStorageLocation>(Val&: *FieldLoc1), Env1, |
| 108 | Loc2: cast<RecordStorageLocation>(Val&: *FieldLoc2), Env2)) |
| 109 | return false; |
| 110 | } else if (Field->getType()->isReferenceType()) { |
| 111 | if (FieldLoc1 != FieldLoc2) |
| 112 | return false; |
| 113 | } else if (Env1.getValue(Loc: *FieldLoc1) != Env2.getValue(Loc: *FieldLoc2)) { |
| 114 | return false; |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | for (const auto &[Name, SynthFieldLoc1] : Loc1.synthetic_fields()) { |
| 119 | if (SynthFieldLoc1->getType()->isRecordType()) { |
| 120 | if (!recordsEqual( |
| 121 | Loc1: *cast<RecordStorageLocation>(Val: SynthFieldLoc1), Env1, |
| 122 | Loc2: cast<RecordStorageLocation>(Val&: Loc2.getSyntheticField(Name)), Env2)) |
| 123 | return false; |
| 124 | } else if (Env1.getValue(Loc: *SynthFieldLoc1) != |
| 125 | Env2.getValue(Loc: Loc2.getSyntheticField(Name))) { |
| 126 | return false; |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | return true; |
| 131 | } |
| 132 | |
| 133 | } // namespace clang::dataflow |
| 134 | |