180 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// Operations on records (structs, classes, and unions).10//11//===----------------------------------------------------------------------===//12 13#include "clang/Analysis/FlowSensitive/RecordOps.h"14#include "clang/AST/Decl.h"15#include "clang/AST/DeclCXX.h"16#include "clang/AST/Type.h"17#include "clang/Analysis/FlowSensitive/ASTOps.h"18#include "clang/Basic/LLVM.h"19#include "llvm/ADT/StringMap.h"20 21#define DEBUG_TYPE "dataflow"22 23namespace clang::dataflow {24 25static void copyField(const ValueDecl &Field, StorageLocation *SrcFieldLoc,26 StorageLocation *DstFieldLoc, RecordStorageLocation &Dst,27 Environment &Env) {28 assert(Field.getType()->isReferenceType() ||29 (SrcFieldLoc != nullptr && DstFieldLoc != nullptr));30 31 if (Field.getType()->isRecordType()) {32 copyRecord(cast<RecordStorageLocation>(*SrcFieldLoc),33 cast<RecordStorageLocation>(*DstFieldLoc), Env);34 } else if (Field.getType()->isReferenceType()) {35 Dst.setChild(Field, SrcFieldLoc);36 } else {37 if (Value *Val = Env.getValue(*SrcFieldLoc))38 Env.setValue(*DstFieldLoc, *Val);39 else40 Env.clearValue(*DstFieldLoc);41 }42}43 44static void copySyntheticField(QualType FieldType, StorageLocation &SrcFieldLoc,45 StorageLocation &DstFieldLoc, Environment &Env) {46 if (FieldType->isRecordType()) {47 copyRecord(cast<RecordStorageLocation>(SrcFieldLoc),48 cast<RecordStorageLocation>(DstFieldLoc), Env);49 } else {50 if (Value *Val = Env.getValue(SrcFieldLoc))51 Env.setValue(DstFieldLoc, *Val);52 else53 Env.clearValue(DstFieldLoc);54 }55}56 57void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst,58 Environment &Env, const QualType TypeToCopy) {59 auto SrcType = Src.getType().getCanonicalType().getUnqualifiedType();60 auto DstType = Dst.getType().getCanonicalType().getUnqualifiedType();61 62 auto SrcDecl = SrcType->getAsCXXRecordDecl();63 auto DstDecl = DstType->getAsCXXRecordDecl();64 65 const CXXRecordDecl *DeclToCopy =66 TypeToCopy.isNull() ? nullptr : TypeToCopy->getAsCXXRecordDecl();67 68 [[maybe_unused]] bool CompatibleTypes =69 SrcType == DstType ||70 (SrcDecl != nullptr && DstDecl != nullptr &&71 (SrcDecl->isDerivedFrom(DstDecl) || DstDecl->isDerivedFrom(SrcDecl) ||72 (DeclToCopy != nullptr && SrcDecl->isDerivedFrom(DeclToCopy) &&73 DstDecl->isDerivedFrom(DeclToCopy))));74 75 LLVM_DEBUG({76 if (!CompatibleTypes) {77 llvm::dbgs() << "Source type " << Src.getType() << "\n";78 llvm::dbgs() << "Destination type " << Dst.getType() << "\n";79 }80 });81 assert(CompatibleTypes);82 83 if (SrcType == DstType || (SrcDecl != nullptr && DstDecl != nullptr &&84 SrcDecl->isDerivedFrom(DstDecl))) {85 // Dst may have children modeled from other derived types than SrcType, e.g.86 // after casts of Dst to other types derived from DstType. Only copy the87 // children and synthetic fields present in both Dst and SrcType.88 const FieldSet FieldsInSrcType =89 Env.getDataflowAnalysisContext().getModeledFields(SrcType);90 for (auto [Field, DstFieldLoc] : Dst.children())91 if (const auto *FieldAsFieldDecl = dyn_cast<FieldDecl>(Field);92 FieldAsFieldDecl && FieldsInSrcType.contains(FieldAsFieldDecl))93 copyField(*Field, Src.getChild(*Field), DstFieldLoc, Dst, Env);94 const llvm::StringMap<QualType> SyntheticFieldsForSrcType =95 Env.getDataflowAnalysisContext().getSyntheticFields(SrcType);96 for (const auto &[Name, DstFieldLoc] : Dst.synthetic_fields())97 if (SyntheticFieldsForSrcType.contains(Name))98 copySyntheticField(DstFieldLoc->getType(), Src.getSyntheticField(Name),99 *DstFieldLoc, Env);100 } else if (SrcDecl != nullptr && DstDecl != nullptr &&101 DstDecl->isDerivedFrom(SrcDecl)) {102 // Src may have children modeled from other derived types than DstType, e.g.103 // after other casts of Src to those types (likely in different branches,104 // but without flow-condition-dependent field modeling). Only copy the105 // children and synthetic fields of Src that are present in DstType.106 const FieldSet FieldsInDstType =107 Env.getDataflowAnalysisContext().getModeledFields(DstType);108 for (auto [Field, SrcFieldLoc] : Src.children()) {109 if (const auto *FieldAsFieldDecl = dyn_cast<FieldDecl>(Field);110 FieldAsFieldDecl && FieldsInDstType.contains(FieldAsFieldDecl))111 copyField(*Field, SrcFieldLoc, Dst.getChild(*Field), Dst, Env);112 }113 const llvm::StringMap<QualType> SyntheticFieldsForDstType =114 Env.getDataflowAnalysisContext().getSyntheticFields(DstType);115 for (const auto &[Name, SrcFieldLoc] : Src.synthetic_fields()) {116 if (SyntheticFieldsForDstType.contains(Name))117 copySyntheticField(SrcFieldLoc->getType(), *SrcFieldLoc,118 Dst.getSyntheticField(Name), Env);119 }120 } else {121 for (const FieldDecl *Field :122 Env.getDataflowAnalysisContext().getModeledFields(TypeToCopy)) {123 copyField(*Field, Src.getChild(*Field), Dst.getChild(*Field), Dst, Env);124 }125 for (const auto &[SyntheticFieldName, SyntheticFieldType] :126 Env.getDataflowAnalysisContext().getSyntheticFields(TypeToCopy)) {127 copySyntheticField(SyntheticFieldType,128 Src.getSyntheticField(SyntheticFieldName),129 Dst.getSyntheticField(SyntheticFieldName), Env);130 }131 }132}133 134bool recordsEqual(const RecordStorageLocation &Loc1, const Environment &Env1,135 const RecordStorageLocation &Loc2, const Environment &Env2) {136 LLVM_DEBUG({137 if (Loc2.getType().getCanonicalType().getUnqualifiedType() !=138 Loc1.getType().getCanonicalType().getUnqualifiedType()) {139 llvm::dbgs() << "Loc1 type " << Loc1.getType() << "\n";140 llvm::dbgs() << "Loc2 type " << Loc2.getType() << "\n";141 }142 });143 assert(Loc2.getType().getCanonicalType().getUnqualifiedType() ==144 Loc1.getType().getCanonicalType().getUnqualifiedType());145 146 for (auto [Field, FieldLoc1] : Loc1.children()) {147 StorageLocation *FieldLoc2 = Loc2.getChild(*Field);148 149 assert(Field->getType()->isReferenceType() ||150 (FieldLoc1 != nullptr && FieldLoc2 != nullptr));151 152 if (Field->getType()->isRecordType()) {153 if (!recordsEqual(cast<RecordStorageLocation>(*FieldLoc1), Env1,154 cast<RecordStorageLocation>(*FieldLoc2), Env2))155 return false;156 } else if (Field->getType()->isReferenceType()) {157 if (FieldLoc1 != FieldLoc2)158 return false;159 } else if (Env1.getValue(*FieldLoc1) != Env2.getValue(*FieldLoc2)) {160 return false;161 }162 }163 164 for (const auto &[Name, SynthFieldLoc1] : Loc1.synthetic_fields()) {165 if (SynthFieldLoc1->getType()->isRecordType()) {166 if (!recordsEqual(167 *cast<RecordStorageLocation>(SynthFieldLoc1), Env1,168 cast<RecordStorageLocation>(Loc2.getSyntheticField(Name)), Env2))169 return false;170 } else if (Env1.getValue(*SynthFieldLoc1) !=171 Env2.getValue(Loc2.getSyntheticField(Name))) {172 return false;173 }174 }175 176 return true;177}178 179} // namespace clang::dataflow180