Files
swift-mirror/lib/IRGen/GenStruct.cpp
2025-11-05 10:42:05 +00:00

1849 lines
76 KiB
C++

//===--- GenStruct.cpp - Swift IR Generation For 'struct' Types -----------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements IR generation for struct types.
//
//===----------------------------------------------------------------------===//
#include "GenStruct.h"
#include "IRGen.h"
#include "swift/AST/ClangModuleLoader.h"
#include "swift/AST/ConformanceLookup.h"
#include "swift/AST/Decl.h"
#include "swift/AST/IRGenOptions.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/ReferenceCounting.h"
#include "swift/AST/SemanticAttrs.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/IRGen/Linking.h"
#include "swift/SIL/SILFunctionBuilder.h"
#include "swift/SIL/SILModule.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/RecordLayout.h"
#include "clang/CodeGen/CodeGenABITypes.h"
#include "clang/CodeGen/SwiftCallingConv.h"
#include "clang/Sema/Sema.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/Error.h"
#include <iterator>
#include "GenDecl.h"
#include "GenMeta.h"
#include "GenRecord.h"
#include "GenType.h"
#include "IRGenFunction.h"
#include "IRGenModule.h"
#include "IndirectTypeInfo.h"
#include "MemberAccessStrategy.h"
#include "MetadataLayout.h"
#include "NonFixedTypeInfo.h"
#include "ResilientTypeInfo.h"
#include "Signature.h"
#include "StructMetadataVisitor.h"
#pragma clang diagnostic ignored "-Winconsistent-missing-override"
using namespace swift;
using namespace irgen;
/// The kinds of TypeInfos implementing struct types.
enum class StructTypeInfoKind {
LoadableStructTypeInfo,
FixedStructTypeInfo,
LoadableClangRecordTypeInfo,
AddressOnlyClangRecordTypeInfo,
NonFixedStructTypeInfo,
ResilientStructTypeInfo
};
static StructTypeInfoKind getStructTypeInfoKind(const TypeInfo &type) {
return (StructTypeInfoKind) type.getSubclassKind();
}
/// If this type has a CXXDestructorDecl, find it and return it. Otherwise,
/// return nullptr.
static clang::CXXDestructorDecl *getCXXDestructor(SILType type) {
auto *structDecl = type.getStructOrBoundGenericStruct();
if (!structDecl || !structDecl->getClangDecl())
return nullptr;
const clang::CXXRecordDecl *cxxRecordDecl =
dyn_cast<clang::CXXRecordDecl>(structDecl->getClangDecl());
if (!cxxRecordDecl)
return nullptr;
return cxxRecordDecl->getDestructor();
}
namespace {
class StructFieldInfo : public RecordField<StructFieldInfo> {
public:
StructFieldInfo(VarDecl *field, const TypeInfo &type)
: RecordField(type), Field(field) {}
/// The field.
VarDecl * const Field;
StringRef getFieldName() const {
return Field->getName().str();
}
SILType getType(IRGenModule &IGM, SILType T) const {
return T.getFieldType(Field, IGM.getSILModule(),
IGM.getMaximalTypeExpansionContext());
}
};
/// A field-info implementation for fields of Clang types.
class ClangFieldInfo : public RecordField<ClangFieldInfo> {
public:
ClangFieldInfo(VarDecl *swiftField, const ElementLayout &layout,
const TypeInfo &typeInfo)
: RecordField(typeInfo), Field(swiftField) {
completeFrom(layout);
}
ClangFieldInfo(VarDecl *swiftField, const ElementLayout &layout,
unsigned explosionBegin, unsigned explosionEnd)
: RecordField(layout, explosionBegin, explosionEnd),
Field(swiftField) {}
VarDecl *Field;
StringRef getFieldName() const {
if (Field) return Field->getName().str();
return "<unimported>";
}
SILType getType(IRGenModule &IGM, SILType T) const {
if (Field)
return T.getFieldType(Field, IGM.getSILModule(),
IGM.getMaximalTypeExpansionContext());
// The Swift-field-less cases use opaque storage, which is
// guaranteed to ignore the type passed to it.
return {};
}
};
/// A common base class for structs.
template <class Impl, class Base, class FieldInfoType = StructFieldInfo>
class StructTypeInfoBase :
public RecordTypeInfo<Impl, Base, FieldInfoType> {
using super = RecordTypeInfo<Impl, Base, FieldInfoType>;
protected:
template <class... As>
StructTypeInfoBase(StructTypeInfoKind kind, As &&...args)
: super(std::forward<As>(args)...) {
super::setSubclassKind((unsigned) kind);
}
using super::asImpl;
public:
const FieldInfoType &getFieldInfo(VarDecl *field) const {
// FIXME: cache the physical field index in the VarDecl.
for (auto &fieldInfo : asImpl().getFields()) {
if (fieldInfo.Field == field)
return fieldInfo;
}
llvm_unreachable("field not in struct?");
}
/// Given a full struct explosion, project out a single field.
virtual void projectFieldFromExplosion(IRGenFunction &IGF, Explosion &in,
VarDecl *field,
Explosion &out) const {
auto &fieldInfo = getFieldInfo(field);
// If the field requires no storage, there's nothing to do.
if (fieldInfo.isEmpty())
return;
// Otherwise, project from the base.
auto fieldRange = fieldInfo.getProjectionRange();
auto elements = in.getRange(fieldRange.first, fieldRange.second);
out.add(elements);
}
/// Given the address of a struct value, project out the address of a
/// single field.
Address projectFieldAddress(IRGenFunction &IGF, Address addr, SILType T,
const FieldInfoType &field) const {
return asImpl().projectFieldAddress(IGF, addr, T, field.Field);
}
/// Given the address of a struct value, project out the address of a
/// single field.
Address projectFieldAddress(IRGenFunction &IGF, Address addr, SILType T,
VarDecl *field) const {
auto &fieldInfo = getFieldInfo(field);
if (fieldInfo.isEmpty()) {
// For fields with empty types, we could return undef.
// But if this is a struct_element_addr which is a result of an optimized
// `MemoryLayout<S>.offset(of: \.field)` we cannot return undef. We have
// to be consistent with `offset(of:)`, which returns 0. Therefore we
// return the base address of the struct.
return addr;
}
auto offsets = asImpl().getNonFixedOffsets(IGF, T);
return fieldInfo.projectAddress(IGF, addr, offsets);
}
/// Return the constant offset of a field as a Int32Ty, or nullptr if the
/// field is not at a fixed offset.
llvm::Constant *getConstantFieldOffset(IRGenModule &IGM,
VarDecl *field) const {
auto &fieldInfo = getFieldInfo(field);
if (fieldInfo.hasFixedByteOffset()) {
return llvm::ConstantInt::get(
IGM.Int32Ty, fieldInfo.getFixedByteOffset().getValue());
}
return nullptr;
}
const TypeInfo *getFieldTypeInfo(IRGenModule &IGM, VarDecl *field) const {
auto &fieldInfo = getFieldInfo(field);
if (fieldInfo.isEmpty())
return nullptr;
return &fieldInfo.getTypeInfo();
}
MemberAccessStrategy getFieldAccessStrategy(IRGenModule &IGM,
SILType T, VarDecl *field) const {
auto &fieldInfo = getFieldInfo(field);
switch (fieldInfo.getKind()) {
case ElementLayout::Kind::Fixed:
case ElementLayout::Kind::Empty:
case ElementLayout::Kind::EmptyTailAllocatedCType:
return MemberAccessStrategy::getDirectFixed(
fieldInfo.getFixedByteOffset());
case ElementLayout::Kind::InitialNonFixedSize:
return MemberAccessStrategy::getDirectFixed(Size(0));
case ElementLayout::Kind::NonFixed:
return asImpl().getNonFixedFieldAccessStrategy(IGM, T, fieldInfo);
}
llvm_unreachable("bad field layout kind");
}
unsigned getFieldIndex(IRGenModule &IGM, VarDecl *field) const {
auto &fieldInfo = getFieldInfo(field);
return fieldInfo.getStructIndex();
}
std::optional<unsigned> getFieldIndexIfNotEmpty(IRGenModule &IGM,
VarDecl *field) const {
auto &fieldInfo = getFieldInfo(field);
if (fieldInfo.isEmpty())
return std::nullopt;
return fieldInfo.getStructIndex();
}
bool isSingleRetainablePointer(ResilienceExpansion expansion,
ReferenceCounting *rc) const override {
// If the type isn't copyable, it doesn't share representation with
// a single-refcounted pointer.
//
// This is sufficient to rule out types with user-defined deinits today,
// since copyable structs are not allowed to define a deinit. If we
// ever added user-defined copy constructors to the language, then we'd
// have to also check that.
if (!this->isCopyable(expansion)) {
return false;
}
auto fields = asImpl().getFields();
if (fields.size() != 1)
return false;
return fields[0].getTypeInfo().isSingleRetainablePointer(expansion, rc);
}
void destroy(IRGenFunction &IGF, Address address, SILType T,
bool isOutlined) const override {
// If the struct has a deinit declared, then call it to destroy the
// value.
if (!tryEmitDestroyUsingDeinit(IGF, address, T)) {
if (!asImpl().areFieldsABIAccessible()) {
emitDestroyCall(IGF, T, address);
return;
}
// Otherwise, perform elementwise destruction of the value.
super::destroy(IGF, address, T, isOutlined);
}
super::fillWithZerosIfSensitive(IGF, address, T);
}
void verify(IRGenTypeVerifierFunction &IGF,
llvm::Value *metadata,
SILType structType) const override {
// Check that constant field offsets we know match
for (auto &field : asImpl().getFields()) {
switch (field.getKind()) {
case ElementLayout::Kind::Fixed: {
// We know the offset at compile time. See whether there's also an
// entry for this field in the field offset vector.
class FindOffsetOfFieldOffsetVector
: public StructMetadataScanner<FindOffsetOfFieldOffsetVector> {
public:
VarDecl *FieldToFind;
Size AddressPoint = Size::invalid();
Size FieldOffset = Size::invalid();
FindOffsetOfFieldOffsetVector(IRGenModule &IGM, VarDecl *Field)
: StructMetadataScanner<FindOffsetOfFieldOffsetVector>(
IGM, cast<StructDecl>(Field->getDeclContext())),
FieldToFind(Field) {}
void noteAddressPoint() {
AddressPoint = this->NextOffset;
}
void addFieldOffset(VarDecl *Field) {
if (Field == FieldToFind) {
FieldOffset = this->NextOffset;
}
StructMetadataScanner<
FindOffsetOfFieldOffsetVector>::addFieldOffset(Field);
}
};
FindOffsetOfFieldOffsetVector scanner(IGF.IGM, field.Field);
scanner.layout();
if (scanner.FieldOffset == Size::invalid()
|| scanner.AddressPoint == Size::invalid())
continue;
// Load the offset from the field offset vector and ensure it matches
// the compiler's idea of the offset.
auto metadataBytes =
IGF.Builder.CreateBitCast(metadata, IGF.IGM.Int8PtrTy);
auto fieldOffsetPtr = IGF.Builder.CreateInBoundsGEP(
IGF.IGM.Int8Ty, metadataBytes,
IGF.IGM.getSize(scanner.FieldOffset - scanner.AddressPoint));
fieldOffsetPtr =
IGF.Builder.CreateBitCast(fieldOffsetPtr, IGF.IGM.PtrTy);
llvm::Value *fieldOffset = IGF.Builder.CreateLoad(
Address(fieldOffsetPtr, IGF.IGM.Int32Ty, Alignment(4)));
fieldOffset = IGF.Builder.CreateZExtOrBitCast(fieldOffset,
IGF.IGM.SizeTy);
IGF.verifyValues(metadata, fieldOffset,
IGF.IGM.getSize(field.getFixedByteOffset()),
Twine("offset of struct field ") + field.getFieldName());
break;
}
case ElementLayout::Kind::Empty:
case ElementLayout::Kind::EmptyTailAllocatedCType:
case ElementLayout::Kind::InitialNonFixedSize:
case ElementLayout::Kind::NonFixed:
continue;
}
}
}
};
/// A type implementation for loadable record types imported from Clang.
class LoadableClangRecordTypeInfo final
: public StructTypeInfoBase<LoadableClangRecordTypeInfo, LoadableTypeInfo,
ClangFieldInfo> {
const clang::RecordDecl *ClangDecl;
bool HasReferenceField;
public:
LoadableClangRecordTypeInfo(ArrayRef<ClangFieldInfo> fields,
unsigned explosionSize, llvm::Type *storageType,
Size size, SpareBitVector &&spareBits,
Alignment align,
IsTriviallyDestroyable_t isTriviallyDestroyable,
IsCopyable_t isCopyable,
const clang::RecordDecl *clangDecl,
bool hasReferenceField)
: StructTypeInfoBase(StructTypeInfoKind::LoadableClangRecordTypeInfo,
fields, explosionSize, FieldsAreABIAccessible,
storageType, size, std::move(spareBits), align,
isTriviallyDestroyable, isCopyable, IsFixedSize,
IsABIAccessible),
ClangDecl(clangDecl), HasReferenceField(hasReferenceField) {}
TypeLayoutEntry
*buildTypeLayoutEntry(IRGenModule &IGM,
SILType T,
bool useStructLayouts) const override {
if (!useStructLayouts) {
return IGM.typeLayoutCache.getOrCreateTypeInfoBasedEntry(*this, T);
}
if (!areFieldsABIAccessible()) {
return IGM.typeLayoutCache.getOrCreateResilientEntry(T);
}
if (getFields().empty()) {
return IGM.typeLayoutCache.getEmptyEntry();
}
std::vector<TypeLayoutEntry *> fields;
for (auto &field : getFields()) {
auto fieldTy = field.getType(IGM, T);
if (!fieldTy) {
return IGM.typeLayoutCache.getOrCreateTypeInfoBasedEntry(*this, T);
}
fields.push_back(
field.getTypeInfo().buildTypeLayoutEntry(IGM, fieldTy, useStructLayouts));
}
assert(!fields.empty() &&
"Empty structs should not be LoadableClangRecordTypeInfo");
// if (fields.size() == 1 && getBestKnownAlignment() == *fields[0]->fixedAlignment(IGM)) {
// return fields[0];
// }
return IGM.typeLayoutCache.getOrCreateAlignedGroupEntry(
fields, T, getBestKnownAlignment().getValue(), *this);
}
void initializeFromParams(IRGenFunction &IGF, Explosion &params,
Address addr, SILType T,
bool isOutlined) const override {
LoadableClangRecordTypeInfo::initialize(IGF, params, addr, isOutlined);
}
void addToAggLowering(IRGenModule &IGM, SwiftAggLowering &lowering,
Size offset) const override {
if (auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(ClangDecl)) {
for (auto base : getBasesAndOffsets(cxxRecordDecl)) {
lowering.addTypedData(base.decl, base.offset.asCharUnits());
}
}
lowering.addTypedData(ClangDecl, offset.asCharUnits());
}
std::nullopt_t getNonFixedOffsets(IRGenFunction &IGF) const {
return std::nullopt;
}
std::nullopt_t getNonFixedOffsets(IRGenFunction &IGF, SILType T) const {
return std::nullopt;
}
MemberAccessStrategy
getNonFixedFieldAccessStrategy(IRGenModule &IGM, SILType T,
const ClangFieldInfo &field) const {
llvm_unreachable("non-fixed field in Clang type?");
}
bool hasReferenceField() const { return HasReferenceField; }
};
class AddressOnlyPointerAuthRecordTypeInfo final
: public StructTypeInfoBase<AddressOnlyPointerAuthRecordTypeInfo,
FixedTypeInfo, ClangFieldInfo> {
const clang::RecordDecl *clangDecl;
void emitCopyWithCopyFunction(IRGenFunction &IGF, SILType T, Address src,
Address dst) const {
auto *copyFunction =
clang::CodeGen::getNonTrivialCStructCopyAssignmentOperator(
IGF.IGM.getClangCGM(), dst.getAlignment(), src.getAlignment(),
/*isVolatile*/ false,
clang::QualType(clangDecl->getTypeForDecl(), 0));
auto *dstValue = dst.getAddress();
auto *srcValue = src.getAddress();
IGF.Builder.CreateCall(copyFunction->getFunctionType(), copyFunction,
{dstValue, srcValue});
}
public:
AddressOnlyPointerAuthRecordTypeInfo(ArrayRef<ClangFieldInfo> fields,
llvm::Type *storageType, Size size,
Alignment align,
IsCopyable_t isCopyable,
const clang::RecordDecl *clangDecl)
: StructTypeInfoBase(StructTypeInfoKind::AddressOnlyClangRecordTypeInfo,
fields, FieldsAreABIAccessible, storageType, size,
// We can't assume any spare bits in a C++ type
// with user-defined special member functions.
SpareBitVector(std::optional<APInt>{
llvm::APInt(size.getValueInBits(), 0)}),
align, IsNotTriviallyDestroyable,
IsNotBitwiseTakable, isCopyable, IsFixedSize,
IsABIAccessible),
clangDecl(clangDecl) {
(void)clangDecl;
}
TypeLayoutEntry
*buildTypeLayoutEntry(IRGenModule &IGM,
SILType T,
bool useStructLayouts) const override {
if (!useStructLayouts) {
return IGM.typeLayoutCache.getOrCreateTypeInfoBasedEntry(*this, T);
}
assert(false && "Implement proper type layout info in the future");
return IGM.typeLayoutCache.getOrCreateTypeInfoBasedEntry(*this, T);
}
void initializeFromParams(IRGenFunction &IGF, Explosion &params,
Address addr, SILType T,
bool isOutlined) const override {
llvm_unreachable("Address-only C++ types must be created by C++ special "
"member functions.");
}
void initializeWithCopy(IRGenFunction &IGF, Address dst, Address src,
SILType T, bool isOutlined) const override {
emitCopyWithCopyFunction(IGF, T, src, dst);
}
void assignWithCopy(IRGenFunction &IGF, Address dst, Address src, SILType T,
bool isOutlined) const override {
emitCopyWithCopyFunction(IGF, T, src, dst);
}
void initializeWithTake(IRGenFunction &IGF, Address dst, Address src,
SILType T, bool isOutlined,
bool zeroizeIfSensitive) const override {
emitCopyWithCopyFunction(IGF, T, src, dst);
destroy(IGF, src, T, isOutlined);
}
void assignWithTake(IRGenFunction &IGF, Address dst, Address src, SILType T,
bool isOutlined) const override {
emitCopyWithCopyFunction(IGF, T, src, dst);
destroy(IGF, src, T, isOutlined);
}
std::nullopt_t getNonFixedOffsets(IRGenFunction &IGF) const {
return std::nullopt;
}
std::nullopt_t getNonFixedOffsets(IRGenFunction &IGF, SILType T) const {
return std::nullopt;
}
MemberAccessStrategy
getNonFixedFieldAccessStrategy(IRGenModule &IGM, SILType T,
const ClangFieldInfo &field) const {
llvm_unreachable("non-fixed field in Clang type?");
}
};
class AddressOnlyCXXClangRecordTypeInfo final
: public StructTypeInfoBase<AddressOnlyCXXClangRecordTypeInfo,
FixedTypeInfo, ClangFieldInfo> {
const clang::RecordDecl *ClangDecl;
const clang::CXXConstructorDecl *findCopyConstructor() const {
const auto *cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(ClangDecl);
if (!cxxRecordDecl)
return nullptr;
for (auto ctor : cxxRecordDecl->ctors()) {
if (ctor->isCopyConstructor() &&
// FIXME: Support default arguments (rdar://142414553)
ctor->getNumParams() == 1 &&
ctor->getAccess() == clang::AS_public && !ctor->isDeleted() &&
!ctor->isIneligibleOrNotSelected())
return ctor;
}
return nullptr;
}
const clang::CXXConstructorDecl *findMoveConstructor() const {
const auto *cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(ClangDecl);
if (!cxxRecordDecl)
return nullptr;
for (auto ctor : cxxRecordDecl->ctors()) {
if (ctor->isMoveConstructor() &&
// FIXME: Support default arguments (rdar://142414553)
ctor->getNumParams() == 1 &&
ctor->getAccess() == clang::AS_public && !ctor->isDeleted() &&
!ctor->isIneligibleOrNotSelected())
return ctor;
}
return nullptr;
}
CanSILFunctionType createCXXCopyConstructorFunctionType(IRGenFunction &IGF,
SILType T) const {
// Create the following function type:
// @convention(c) (UnsafePointer<T>) -> @out T
// This is how clang *would* import the copy constructor. So, later, when
// we pass it to "emitCXXConstructorThunkIfNeeded" we get a thunk with
// the following LLVM function type:
// void (%struct.T* %this, %struct.T* %0)
auto ptrTypeDecl =
IGF.getSILModule().getASTContext().getUnsafePointerDecl();
auto sig = ptrTypeDecl->getGenericSignature();
// Map the generic parameter to T
auto subst = SubstitutionMap::get(sig, {T.getASTType()},
LookUpConformanceInModule());
auto ptrType = ptrTypeDecl->getDeclaredInterfaceType().subst(subst);
SILParameterInfo ptrParam(ptrType->getCanonicalType(),
ParameterConvention::Direct_Unowned);
SILResultInfo result(T.getASTType(), ResultConvention::Indirect);
auto clangFnType = T.getASTContext().getCanonicalClangFunctionType(
{ptrParam}, result, SILFunctionTypeRepresentation::CFunctionPointer);
auto extInfo = SILExtInfoBuilder()
.withClangFunctionType(clangFnType)
.withRepresentation(
SILFunctionTypeRepresentation::CFunctionPointer)
.build();
return SILFunctionType::get(
GenericSignature(),
extInfo,
SILCoroutineKind::None,
/*callee=*/ParameterConvention::Direct_Unowned,
/*params*/ {ptrParam},
/*yields*/ {}, /*results*/ {result},
/*error*/ std::nullopt,
/*pattern subs*/ SubstitutionMap(),
/*invocation subs*/ SubstitutionMap(), IGF.IGM.Context);
}
void emitCopyWithCopyConstructor(
IRGenFunction &IGF, SILType T,
const clang::CXXConstructorDecl *copyConstructor, llvm::Value *src,
llvm::Value *dest) const {
auto fnType = createCXXCopyConstructorFunctionType(IGF, T);
auto globalDecl =
clang::GlobalDecl(copyConstructor, clang::Ctor_Complete);
auto &ctx = IGF.IGM.Context;
auto *importer = static_cast<ClangImporter *>(ctx.getClangModuleLoader());
auto &diagEngine = importer->getClangSema().getDiagnostics();
clang::DiagnosticErrorTrap trap(diagEngine);
auto clangFnAddr =
IGF.IGM.getAddrOfClangGlobalDecl(globalDecl, NotForDefinition);
if (trap.hasErrorOccurred()) {
SourceLoc copyConstructorLoc =
importer->importSourceLocation(copyConstructor->getLocation());
auto *recordDecl = copyConstructor->getParent();
ctx.Diags.diagnose(copyConstructorLoc, diag::failed_emit_copy,
recordDecl);
bool hasCopyableIfAttr =
recordDecl->hasAttrs() &&
llvm::any_of(recordDecl->getAttrs(), [&](clang::Attr *attr) {
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
StringRef attrStr = swiftAttr->getAttribute();
assert(!attrStr.starts_with("~Copyable") &&
"Trying to emit copy of a type annotated with "
"'SWIFT_NONCOPYABLE'?");
if (attrStr.starts_with("copyable_if:"))
return true;
}
return false;
});
bool hasRequiresClause =
!copyConstructor->getTrailingRequiresClause().isNull();
if (hasRequiresClause || hasCopyableIfAttr) {
ctx.Diags.diagnose(copyConstructorLoc, diag::maybe_missing_annotation,
recordDecl);
ctx.Diags.diagnose(copyConstructorLoc, diag::maybe_missing_parameter,
hasCopyableIfAttr, recordDecl);
} else {
ctx.Diags.diagnose(copyConstructorLoc, diag::use_requires_expression);
ctx.Diags.diagnose(copyConstructorLoc, diag::annotate_copyable_if);
}
if (!copyConstructor->isUserProvided()) {
ctx.Diags.diagnose(copyConstructorLoc, diag::annotate_non_copyable);
}
}
auto callee = cast<llvm::Function>(clangFnAddr->stripPointerCasts());
Signature signature = IGF.IGM.getSignature(fnType, copyConstructor);
std::string name = "__swift_cxx_copy_ctor" + callee->getName().str();
auto *origClangFnAddr = clangFnAddr;
clangFnAddr = emitCXXConstructorThunkIfNeeded(
IGF.IGM, signature, copyConstructor, name, clangFnAddr);
callee = cast<llvm::Function>(clangFnAddr);
llvm::Value *args[] = {dest, src};
if (clangFnAddr == origClangFnAddr) {
// Ensure we can use 'invoke' to trap on uncaught exceptions when
// calling original copy constructor without going through the thunk.
emitCXXConstructorCall(IGF, copyConstructor, callee->getFunctionType(),
callee, args);
return;
}
// Check if we're calling a thunk that traps on exception thrown from copy
// constructor.
if (IGF.IGM.emittedForeignFunctionThunksWithExceptionTraps.count(callee))
IGF.setCallsThunksWithForeignExceptionTraps();
IGF.Builder.CreateCall(callee->getFunctionType(), callee, args);
}
public:
AddressOnlyCXXClangRecordTypeInfo(ArrayRef<ClangFieldInfo> fields,
llvm::Type *storageType, Size size,
Alignment align,
IsCopyable_t isCopyable,
const clang::RecordDecl *clangDecl)
: StructTypeInfoBase(StructTypeInfoKind::AddressOnlyClangRecordTypeInfo,
fields, FieldsAreABIAccessible, storageType, size,
// We can't assume any spare bits in a C++ type
// with user-defined special member functions.
SpareBitVector(std::optional<APInt>{
llvm::APInt(size.getValueInBits(), 0)}),
align, IsNotTriviallyDestroyable,
IsNotBitwiseTakable,
isCopyable, IsFixedSize, IsABIAccessible),
ClangDecl(clangDecl) {
(void)ClangDecl;
}
void destroy(IRGenFunction &IGF, Address address, SILType T,
bool isOutlined) const override {
auto *destructor = getCXXDestructor(T);
// If the destructor is trivial, clang will assert when we call
// `emitCXXDestructorCall` so, just let Swift handle this destructor.
if (!destructor || destructor->isTrivial()) {
// If we didn't find a destructor to call, bail out to the parent
// implementation.
StructTypeInfoBase<AddressOnlyCXXClangRecordTypeInfo, FixedTypeInfo,
ClangFieldInfo>::destroy(IGF, address, T,
isOutlined);
return;
}
IGF.IGM.ensureImplicitCXXDestructorBodyIsDefined(destructor);
clang::GlobalDecl destructorGlobalDecl(destructor, clang::Dtor_Complete);
auto *destructorFnAddr =
cast<llvm::Function>(IGF.IGM.getAddrOfClangGlobalDecl(
destructorGlobalDecl, NotForDefinition));
SmallVector<llvm::Value *, 2> args;
auto *thisArg = address.getAddress();
args.push_back(thisArg);
llvm::Value *implicitParam =
clang::CodeGen::getCXXDestructorImplicitParam(
IGF.IGM.getClangCGM(), IGF.Builder.GetInsertBlock(),
IGF.Builder.GetInsertPoint(), destructor, clang::Dtor_Complete,
false, false);
if (implicitParam) {
implicitParam = IGF.coerceValue(implicitParam,
destructorFnAddr->getArg(1)->getType(),
IGF.IGM.DataLayout);
args.push_back(implicitParam);
}
bool canThrow = false;
if (IGF.IGM.isForeignExceptionHandlingEnabled()) {
if (!IGF.IGM.isCxxNoThrow(destructor, /*defaultNoThrow=*/true))
canThrow = true;
}
if (canThrow) {
IGF.createExceptionTrapScope([&](llvm::BasicBlock *invokeNormalDest,
llvm::BasicBlock *invokeUnwindDest) {
IGF.Builder.createInvoke(destructorFnAddr->getFunctionType(),
destructorFnAddr, args, invokeNormalDest,
invokeUnwindDest);
});
return;
}
IGF.Builder.CreateCall(destructorFnAddr->getFunctionType(),
destructorFnAddr, args);
}
TypeLayoutEntry
*buildTypeLayoutEntry(IRGenModule &IGM,
SILType T,
bool useStructLayouts) const override {
if (!useStructLayouts || getCXXDestructor(T) ||
!areFieldsABIAccessible()) {
return IGM.typeLayoutCache.getOrCreateTypeInfoBasedEntry(*this, T);
}
std::vector<TypeLayoutEntry *> fields;
for (auto &field : getFields()) {
auto fieldTy = field.getType(IGM, T);
if (!fieldTy) {
return IGM.typeLayoutCache.getOrCreateTypeInfoBasedEntry(*this, T);
}
fields.push_back(
field.getTypeInfo().buildTypeLayoutEntry(IGM, fieldTy, useStructLayouts));
}
assert(!fields.empty() &&
"Empty structs should not be AddressOnlyRecordTypeInfo");
if (fields.size() == 1 && getBestKnownAlignment() == *fields[0]->fixedAlignment(IGM)) {
return fields[0];
}
return IGM.typeLayoutCache.getOrCreateAlignedGroupEntry(
fields, T, getBestKnownAlignment().getValue(), *this);
}
void initializeFromParams(IRGenFunction &IGF, Explosion &params,
Address addr, SILType T,
bool isOutlined) const override {
llvm_unreachable("Address-only C++ types must be created by C++ special "
"member functions.");
}
void initializeWithCopy(IRGenFunction &IGF, Address destAddr,
Address srcAddr, SILType T,
bool isOutlined) const override {
if (auto copyConstructor = findCopyConstructor()) {
emitCopyWithCopyConstructor(IGF, T, copyConstructor,
srcAddr.getAddress(),
destAddr.getAddress());
return;
}
StructTypeInfoBase<AddressOnlyCXXClangRecordTypeInfo, FixedTypeInfo,
ClangFieldInfo>::initializeWithCopy(IGF, destAddr,
srcAddr, T,
isOutlined);
}
void assignWithCopy(IRGenFunction &IGF, Address destAddr, Address srcAddr,
SILType T, bool isOutlined) const override {
if (auto copyConstructor = findCopyConstructor()) {
destroy(IGF, destAddr, T, isOutlined);
emitCopyWithCopyConstructor(IGF, T, copyConstructor,
srcAddr.getAddress(),
destAddr.getAddress());
return;
}
StructTypeInfoBase<AddressOnlyCXXClangRecordTypeInfo, FixedTypeInfo,
ClangFieldInfo>::assignWithCopy(IGF, destAddr, srcAddr,
T, isOutlined);
}
void initializeWithTake(IRGenFunction &IGF, Address dest, Address src,
SILType T, bool isOutlined,
bool zeroizeIfSensitive) const override {
if (auto moveConstructor = findMoveConstructor()) {
emitCopyWithCopyConstructor(IGF, T, moveConstructor,
src.getAddress(),
dest.getAddress());
destroy(IGF, src, T, isOutlined);
return;
}
if (auto copyConstructor = findCopyConstructor()) {
emitCopyWithCopyConstructor(IGF, T, copyConstructor,
src.getAddress(),
dest.getAddress());
destroy(IGF, src, T, isOutlined);
return;
}
StructTypeInfoBase<AddressOnlyCXXClangRecordTypeInfo, FixedTypeInfo,
ClangFieldInfo>::initializeWithTake(IGF, dest, src, T,
isOutlined, zeroizeIfSensitive);
}
void assignWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T,
bool isOutlined) const override {
if (auto moveConstructor = findMoveConstructor()) {
destroy(IGF, dest, T, isOutlined);
emitCopyWithCopyConstructor(IGF, T, moveConstructor,
src.getAddress(),
dest.getAddress());
destroy(IGF, src, T, isOutlined);
return;
}
if (auto copyConstructor = findCopyConstructor()) {
destroy(IGF, dest, T, isOutlined);
emitCopyWithCopyConstructor(IGF, T, copyConstructor,
src.getAddress(),
dest.getAddress());
destroy(IGF, src, T, isOutlined);
return;
}
StructTypeInfoBase<AddressOnlyCXXClangRecordTypeInfo, FixedTypeInfo,
ClangFieldInfo>::assignWithTake(IGF, dest, src, T,
isOutlined);
}
std::nullopt_t getNonFixedOffsets(IRGenFunction &IGF) const {
return std::nullopt;
}
std::nullopt_t getNonFixedOffsets(IRGenFunction &IGF, SILType T) const {
return std::nullopt;
}
MemberAccessStrategy
getNonFixedFieldAccessStrategy(IRGenModule &IGM, SILType T,
const ClangFieldInfo &field) const {
llvm_unreachable("non-fixed field in Clang type?");
}
};
/// A type implementation for loadable struct types.
class LoadableStructTypeInfo final
: public StructTypeInfoBase<LoadableStructTypeInfo, LoadableTypeInfo> {
using super = StructTypeInfoBase<LoadableStructTypeInfo, LoadableTypeInfo>;
public:
LoadableStructTypeInfo(ArrayRef<StructFieldInfo> fields,
FieldsAreABIAccessible_t areFieldsABIAccessible,
unsigned explosionSize,
llvm::Type *storageType, Size size,
SpareBitVector &&spareBits,
Alignment align,
IsTriviallyDestroyable_t isTriviallyDestroyable,
IsCopyable_t isCopyable,
IsFixedSize_t alwaysFixedSize,
IsABIAccessible_t isABIAccessible)
: StructTypeInfoBase(StructTypeInfoKind::LoadableStructTypeInfo,
fields, explosionSize, areFieldsABIAccessible,
storageType, size, std::move(spareBits),
align, isTriviallyDestroyable,
isCopyable,
alwaysFixedSize, isABIAccessible)
{}
void addToAggLowering(IRGenModule &IGM, SwiftAggLowering &lowering,
Size offset) const override {
for (auto &field : getFields()) {
auto fieldOffset = offset + field.getFixedByteOffset();
cast<LoadableTypeInfo>(field.getTypeInfo())
.addToAggLowering(IGM, lowering, fieldOffset);
}
}
TypeLayoutEntry
*buildTypeLayoutEntry(IRGenModule &IGM,
SILType T,
bool useStructLayouts) const override {
if (!useStructLayouts) {
return IGM.typeLayoutCache.getOrCreateTypeInfoBasedEntry(*this, T);
}
if (!areFieldsABIAccessible()) {
return IGM.typeLayoutCache.getOrCreateResilientEntry(T);
}
if (getFields().empty()) {
return IGM.typeLayoutCache.getEmptyEntry();
}
std::vector<TypeLayoutEntry *> fields;
for (auto &field : getFields()) {
auto fieldTy = field.getType(IGM, T);
fields.push_back(
field.getTypeInfo().buildTypeLayoutEntry(IGM, fieldTy, useStructLayouts));
}
// if (fields.size() == 1 && isFixedSize() &&
// getBestKnownAlignment() == *fields[0]->fixedAlignment(IGM)) {
// return fields[0];
// }
return IGM.typeLayoutCache.getOrCreateAlignedGroupEntry(
fields, T, getBestKnownAlignment().getValue(), *this);
}
void initializeFromParams(IRGenFunction &IGF, Explosion &params,
Address addr, SILType T,
bool isOutlined) const override {
LoadableStructTypeInfo::initialize(IGF, params, addr, isOutlined);
}
std::nullopt_t getNonFixedOffsets(IRGenFunction &IGF) const {
return std::nullopt;
}
std::nullopt_t getNonFixedOffsets(IRGenFunction &IGF, SILType T) const {
return std::nullopt;
}
MemberAccessStrategy
getNonFixedFieldAccessStrategy(IRGenModule &IGM, SILType T,
const StructFieldInfo &field) const {
llvm_unreachable("non-fixed field in loadable type?");
}
void consume(IRGenFunction &IGF, Explosion &explosion,
Atomicity atomicity, SILType T) const override {
// If the struct has a deinit declared, then call it to consume the
// value.
if (tryEmitConsumeUsingDeinit(IGF, explosion, T)) {
return;
}
if (!areFieldsABIAccessible()) {
auto temporary = allocateStack(IGF, T, "deinit.arg").getAddress();
initialize(IGF, explosion, temporary, /*outlined*/false);
emitDestroyCall(IGF, T, temporary);
return;
}
// Otherwise, do elementwise destruction of the value.
return super::consume(IGF, explosion, atomicity, T);
}
};
/// A type implementation for non-loadable but fixed-size struct types.
class FixedStructTypeInfo final
: public StructTypeInfoBase<FixedStructTypeInfo,
IndirectTypeInfo<FixedStructTypeInfo,
FixedTypeInfo>> {
public:
// FIXME: Spare bits between struct members.
FixedStructTypeInfo(ArrayRef<StructFieldInfo> fields,
FieldsAreABIAccessible_t areFieldsABIAccessible,
llvm::Type *T,
Size size, SpareBitVector &&spareBits,
Alignment align,
IsTriviallyDestroyable_t isTriviallyDestroyable,
IsBitwiseTakable_t isBT,
IsCopyable_t isCopyable,
IsFixedSize_t alwaysFixedSize,
IsABIAccessible_t isABIAccessible)
: StructTypeInfoBase(StructTypeInfoKind::FixedStructTypeInfo,
fields, areFieldsABIAccessible,
T, size, std::move(spareBits), align,
isTriviallyDestroyable, isBT, isCopyable,
alwaysFixedSize, isABIAccessible)
{}
TypeLayoutEntry
*buildTypeLayoutEntry(IRGenModule &IGM,
SILType T,
bool useStructLayouts) const override {
if (!useStructLayouts) {
return IGM.typeLayoutCache.getOrCreateTypeInfoBasedEntry(*this, T);
}
if (!areFieldsABIAccessible()) {
return IGM.typeLayoutCache.getOrCreateResilientEntry(T);
}
// If we have a raw layout struct who is fixed size, it means the
// layout of the struct is fully concrete.
if (auto rawLayout = T.getRawLayout()) {
// Defer to this fixed type info for type layout if the raw layout
// specifies size and alignment.
if (rawLayout->getSizeAndAlignment()) {
return IGM.typeLayoutCache.getOrCreateTypeInfoBasedEntry(*this, T);
}
auto likeType = T.getRawLayoutSubstitutedLikeType();
auto loweredLikeType = IGM.getLoweredType(likeType);
auto likeTypeLayout = IGM.getTypeInfo(loweredLikeType)
.buildTypeLayoutEntry(IGM, loweredLikeType, useStructLayouts);
// If we're an array, use the ArrayLayoutEntry.
if (rawLayout->getArrayLikeTypeAndCount()) {
auto countType = T.getRawLayoutSubstitutedCountType()->getCanonicalType();
return IGM.typeLayoutCache.getOrCreateArrayEntry(likeTypeLayout,
loweredLikeType,
countType);
}
// Otherwise, this is just going to use the same layout entry as the
// like type.
return likeTypeLayout;
}
std::vector<TypeLayoutEntry *> fields;
for (auto &field : getFields()) {
auto fieldTy = field.getType(IGM, T);
fields.push_back(
field.getTypeInfo().buildTypeLayoutEntry(IGM, fieldTy, useStructLayouts));
}
assert(!fields.empty() &&
"Empty structs should not be FixedStructTypeInfo");
// if (fields.size() == 1 && getBestKnownAlignment() == *fields[0]->fixedAlignment(IGM)) {
// return fields[0];
// }
return IGM.typeLayoutCache.getOrCreateAlignedGroupEntry(
fields, T, getBestKnownAlignment().getValue(), *this);
}
std::nullopt_t getNonFixedOffsets(IRGenFunction &IGF) const {
return std::nullopt;
}
std::nullopt_t getNonFixedOffsets(IRGenFunction &IGF, SILType T) const {
return std::nullopt;
}
MemberAccessStrategy
getNonFixedFieldAccessStrategy(IRGenModule &IGM, SILType T,
const StructFieldInfo &field) const {
llvm_unreachable("non-fixed field in fixed struct?");
}
};
/// Accessor for the non-fixed offsets of a struct type.
class StructNonFixedOffsets : public NonFixedOffsetsImpl {
SILType TheStruct;
public:
StructNonFixedOffsets(SILType type) : TheStruct(type) {
assert(TheStruct.getStructOrBoundGenericStruct());
}
llvm::Value *getOffsetForIndex(IRGenFunction &IGF, unsigned index) override {
auto &layout =
IGF.IGM.getMetadataLayout(TheStruct.getStructOrBoundGenericStruct());
auto offset = layout.getFieldOffset(
IGF, layout.getDecl()->getStoredProperties()[index]);
llvm::Value *metadata = IGF.emitTypeMetadataRefForLayout(TheStruct);
auto field = IGF.emitAddressAtOffset(metadata, offset, IGF.IGM.Int32Ty,
IGF.IGM.getPointerAlignment());
return IGF.Builder.CreateLoad(field);
}
MemberAccessStrategy getFieldAccessStrategy(IRGenModule &IGM,
unsigned nonFixedIndex) {
auto start =
IGM.getMetadataLayout(TheStruct.getStructOrBoundGenericStruct())
.getFieldOffsetVectorOffset();
// FIXME: Handle resilience
auto indirectOffset = start.getStatic() +
(IGM.getPointerSize() * nonFixedIndex);
return MemberAccessStrategy::getIndirectFixed(indirectOffset,
MemberAccessStrategy::OffsetKind::Bytes_Word);
}
};
/// A type implementation for non-fixed struct types.
class NonFixedStructTypeInfo final
: public StructTypeInfoBase<NonFixedStructTypeInfo,
WitnessSizedTypeInfo<NonFixedStructTypeInfo>>
{
public:
NonFixedStructTypeInfo(ArrayRef<StructFieldInfo> fields,
FieldsAreABIAccessible_t fieldsAccessible,
llvm::Type *T,
Alignment align,
IsTriviallyDestroyable_t isTriviallyDestroyable,
IsBitwiseTakable_t isBT,
IsCopyable_t isCopyable,
IsABIAccessible_t structAccessible)
: StructTypeInfoBase(StructTypeInfoKind::NonFixedStructTypeInfo,
fields, fieldsAccessible,
T, align, isTriviallyDestroyable, isBT, isCopyable,
structAccessible) {
}
TypeLayoutEntry
*buildTypeLayoutEntry(IRGenModule &IGM,
SILType T,
bool useStructLayouts) const override {
if (!areFieldsABIAccessible()) {
return IGM.typeLayoutCache.getOrCreateResilientEntry(T);
}
// If we have a raw layout struct who is non-fixed size, it means the
// layout of the struct is dependent on the archetype of the thing it's
// like.
if (auto rawLayout = T.getRawLayout()) {
// Note: We don't have to handle the size and alignment case here for
// raw layout because those are always fixed, so only dependent layouts
// will be non-fixed.
auto likeType = T.getRawLayoutSubstitutedLikeType();
auto loweredLikeType = IGM.getLoweredType(likeType->getCanonicalType());
auto likeTypeLayout = IGM.getTypeInfo(loweredLikeType)
.buildTypeLayoutEntry(IGM, loweredLikeType, useStructLayouts);
// If we're an array, use the ArrayLayoutEntry.
if (rawLayout->getArrayLikeTypeAndCount()) {
auto countType = T.getRawLayoutSubstitutedCountType()->getCanonicalType();
return IGM.typeLayoutCache.getOrCreateArrayEntry(likeTypeLayout,
loweredLikeType,
countType);
}
// Otherwise, this is just going to use the same layout entry as the
// like type.
return likeTypeLayout;
}
std::vector<TypeLayoutEntry *> fields;
for (auto &field : getFields()) {
auto fieldTy = field.getType(IGM, T);
fields.push_back(
field.getTypeInfo().buildTypeLayoutEntry(IGM, fieldTy, useStructLayouts));
}
assert(!fields.empty() &&
"Empty structs should not be NonFixedStructTypeInfo");
// if (fields.size() == 1 && getBestKnownAlignment() > Alignment(1)) {
// return fields[0];
// }
return IGM.typeLayoutCache.getOrCreateAlignedGroupEntry(
fields, T, getBestKnownAlignment().getValue(), *this);
}
// We have an indirect schema.
void getSchema(ExplosionSchema &s) const override {
s.add(ExplosionSchema::Element::forAggregate(getStorageType(),
getBestKnownAlignment()));
}
StructNonFixedOffsets
getNonFixedOffsets(IRGenFunction &IGF, SILType T) const {
return StructNonFixedOffsets(T);
}
MemberAccessStrategy
getNonFixedFieldAccessStrategy(IRGenModule &IGM, SILType T,
const StructFieldInfo &field) const {
return StructNonFixedOffsets(T).getFieldAccessStrategy(IGM,
field.getNonFixedElementIndex());
}
llvm::Value *getEnumTagSinglePayload(IRGenFunction &IGF,
llvm::Value *numEmptyCases,
Address structAddr,
SILType structType,
bool isOutlined) const override {
// If we're not emitting the value witness table's implementation,
// just call that.
if (!isOutlined) {
return emitGetEnumTagSinglePayloadCall(IGF, structType, numEmptyCases,
structAddr);
}
return emitGetEnumTagSinglePayloadGenericCall(IGF, structType, *this,
numEmptyCases, structAddr,
[this,structType](IRGenFunction &IGF, Address structAddr,
llvm::Value *structNumXI) {
return withExtraInhabitantProvidingField(IGF, structAddr, structType,
structNumXI, IGF.IGM.Int32Ty,
[&](const FieldImpl &field, llvm::Value *numXI) -> llvm::Value* {
Address fieldAddr = asImpl().projectFieldAddress(
IGF, structAddr, structType, field);
auto fieldTy = field.getType(IGF.IGM, structType);
return field.getTypeInfo()
.getExtraInhabitantTagDynamic(IGF, fieldAddr, fieldTy,
numXI, /*outlined*/ false);
});
});
}
void storeEnumTagSinglePayload(IRGenFunction &IGF,
llvm::Value *whichCase,
llvm::Value *numEmptyCases,
Address structAddr,
SILType structType,
bool isOutlined) const override {
// If we're not emitting the value witness table's implementation,
// just call that.
if (!isOutlined) {
return emitStoreEnumTagSinglePayloadCall(IGF, structType, whichCase,
numEmptyCases, structAddr);
}
emitStoreEnumTagSinglePayloadGenericCall(IGF, structType, *this,
whichCase, numEmptyCases,
structAddr,
[this,structType](IRGenFunction &IGF, Address structAddr,
llvm::Value *tag, llvm::Value *structNumXI) {
withExtraInhabitantProvidingField(IGF, structAddr, structType,
structNumXI, IGF.IGM.VoidTy,
[&](const FieldImpl &field, llvm::Value *numXI) -> llvm::Value* {
Address fieldAddr = asImpl().projectFieldAddress(
IGF, structAddr, structType, field);
auto fieldTy = field.getType(IGF.IGM, structType);
field.getTypeInfo()
.storeExtraInhabitantTagDynamic(IGF, tag, fieldAddr, fieldTy,
/*outlined*/ false);
return nullptr;
});
});
}
};
class StructTypeBuilder :
public RecordTypeBuilder<StructTypeBuilder, StructFieldInfo, VarDecl*> {
llvm::StructType *StructTy;
CanType TheStruct;
public:
StructTypeBuilder(IRGenModule &IGM, llvm::StructType *structTy,
CanType type) :
RecordTypeBuilder(IGM), StructTy(structTy), TheStruct(type) {
}
LoadableStructTypeInfo *createLoadable(ArrayRef<StructFieldInfo> fields,
FieldsAreABIAccessible_t areFieldsABIAccessible,
StructLayout &&layout,
unsigned explosionSize) {
auto isABIAccessible = isTypeABIAccessibleIfFixedSize(IGM, TheStruct);
return LoadableStructTypeInfo::create(fields,
areFieldsABIAccessible,
explosionSize,
layout.getType(),
layout.getSize(),
std::move(layout.getSpareBits()),
layout.getAlignment(),
layout.isTriviallyDestroyable(),
layout.isCopyable(),
layout.isAlwaysFixedSize(),
isABIAccessible);
}
FixedStructTypeInfo *createFixed(ArrayRef<StructFieldInfo> fields,
FieldsAreABIAccessible_t areFieldsABIAccessible,
StructLayout &&layout) {
auto isABIAccessible = isTypeABIAccessibleIfFixedSize(IGM, TheStruct);
return FixedStructTypeInfo::create(fields, areFieldsABIAccessible,
layout.getType(),
layout.getSize(),
std::move(layout.getSpareBits()),
layout.getAlignment(),
layout.isTriviallyDestroyable(),
layout.isBitwiseTakable(),
layout.isCopyable(),
layout.isAlwaysFixedSize(),
isABIAccessible);
}
NonFixedStructTypeInfo *createNonFixed(ArrayRef<StructFieldInfo> fields,
FieldsAreABIAccessible_t fieldsAccessible,
StructLayout &&layout) {
auto structAccessible = IsABIAccessible_t(
IGM.getSILModule().isTypeMetadataAccessible(TheStruct));
return NonFixedStructTypeInfo::create(fields, fieldsAccessible,
layout.getType(),
layout.getAlignment(),
layout.isTriviallyDestroyable(),
layout.isBitwiseTakable(),
layout.isCopyable(),
structAccessible);
}
StructFieldInfo getFieldInfo(unsigned index,
VarDecl *field, const TypeInfo &fieldTI) {
return StructFieldInfo(field, fieldTI);
}
SILType getType(VarDecl *field) {
assert(field->getDeclContext() == TheStruct->getAnyNominal());
auto silType = SILType::getPrimitiveAddressType(TheStruct);
return silType.getFieldType(
field, IGM.getSILModule(),
IGM.getMaximalTypeExpansionContext());
}
StructLayout performLayout(ArrayRef<const TypeInfo *> fieldTypes) {
return StructLayout(IGM, TheStruct, LayoutKind::NonHeapObject,
LayoutStrategy::Optimal, fieldTypes, StructTy);
}
};
/// A class for lowering Clang records.
class ClangRecordLowering {
IRGenModule &IGM;
StructDecl *SwiftDecl;
SILType SwiftType;
const clang::RecordDecl *ClangDecl;
const clang::ASTContext &ClangContext;
const clang::ASTRecordLayout &ClangLayout;
const Size TotalStride;
const Alignment TotalAlignment;
SpareBitVector SpareBits;
SmallVector<llvm::Type *, 8> LLVMFields;
SmallVector<ClangFieldInfo, 8> FieldInfos;
Size NextOffset = Size(0);
Size SubobjectAdjustment = Size(0);
unsigned NextExplosionIndex = 0;
// Types that are trivial in C++ but are containing fields to reference types
// are not trivial in Swift, they cannot be copied using memcpy as we need to
// do the proper retain operations.
bool hasReferenceField = false;
public:
ClangRecordLowering(IRGenModule &IGM, StructDecl *swiftDecl,
const clang::RecordDecl *clangDecl,
SILType swiftType)
: IGM(IGM), SwiftDecl(swiftDecl), SwiftType(swiftType),
ClangDecl(clangDecl), ClangContext(clangDecl->getASTContext()),
ClangLayout(ClangContext.getASTRecordLayout(clangDecl)),
TotalStride(Size(ClangLayout.getSize().getQuantity())),
TotalAlignment(IGM.getCappedAlignment(
Alignment(ClangLayout.getAlignment()))) {
}
void collectRecordFields() {
if (ClangDecl->isUnion()) {
collectUnionFields();
} else {
collectBases(ClangDecl);
collectStructFields(ClangDecl);
}
}
const TypeInfo *createTypeInfo(llvm::StructType *llvmType) {
llvmType->setBody(LLVMFields, /*packed*/ true);
if (SwiftType.getStructOrBoundGenericStruct()->isCxxNonTrivial()) {
return AddressOnlyCXXClangRecordTypeInfo::create(
FieldInfos, llvmType, TotalStride, TotalAlignment,
(SwiftDecl && !SwiftDecl->canBeCopyable())
? IsNotCopyable : IsCopyable,
ClangDecl);
}
if (SwiftType.getStructOrBoundGenericStruct()->isNonTrivialPtrAuth()) {
return AddressOnlyPointerAuthRecordTypeInfo::create(
FieldInfos, llvmType, TotalStride, TotalAlignment,
(SwiftDecl && !SwiftDecl->canBeCopyable())
? IsNotCopyable : IsCopyable,
ClangDecl);
}
return LoadableClangRecordTypeInfo::create(
FieldInfos, NextExplosionIndex, llvmType, TotalStride,
std::move(SpareBits), TotalAlignment,
(SwiftDecl &&
(SwiftDecl->getValueTypeDestructor() || hasReferenceField))
? IsNotTriviallyDestroyable
: IsTriviallyDestroyable,
(SwiftDecl && !SwiftDecl->canBeCopyable()) ? IsNotCopyable : IsCopyable,
ClangDecl, hasReferenceField);
}
private:
/// Collect all the fields of a union.
void collectUnionFields() {
addOpaqueField(Size(0), TotalStride);
}
static bool isImportOfClangField(VarDecl *swiftField,
const clang::FieldDecl *clangField) {
assert(swiftField->hasClangNode());
return (swiftField->getClangNode().castAsDecl() == clangField);
}
void collectBases(const clang::RecordDecl *decl) {
if (auto cxxRecord = dyn_cast<clang::CXXRecordDecl>(decl)) {
auto bases = getBasesAndOffsets(cxxRecord);
for (auto base : bases) {
SubobjectAdjustment += base.offset;
collectBases(base.decl);
collectStructFields(base.decl);
SubobjectAdjustment -= base.offset;
}
}
}
void collectStructFields(const clang::RecordDecl *decl) {
auto cfi = decl->field_begin(), cfe = decl->field_end();
const auto &layout = ClangContext.getASTRecordLayout(decl);
auto swiftProperties = SwiftDecl->getStoredProperties();
auto sfi = swiftProperties.begin(), sfe = swiftProperties.end();
// When collecting fields from the base subobjects, we do not have corresponding swift
// stored properties.
bool isBaseSubobject = decl != ClangDecl;
if (isBaseSubobject)
sfi = swiftProperties.end();
while (cfi != cfe) {
const clang::FieldDecl *clangField = *cfi++;
// Bitfields are currently never mapped, but that doesn't mean
// we don't have to copy them.
if (clangField->isBitField()) {
// Collect all of the following bitfields.
unsigned bitStart =
layout.getFieldOffset(clangField->getFieldIndex());
unsigned bitEnd = bitStart + clangField->getBitWidthValue();
while (cfi != cfe && (*cfi)->isBitField()) {
clangField = *cfi++;
unsigned nextStart =
layout.getFieldOffset(clangField->getFieldIndex());
assert(nextStart >= bitEnd && "laying out bit-fields out of order?");
// In a heuristic effort to reduce the number of weird-sized
// fields, whenever we see a bitfield starting on a 32-bit
// boundary, start a new storage unit.
if (nextStart % 32 == 0) {
addOpaqueBitField(bitStart, bitEnd);
bitStart = nextStart;
}
bitEnd = nextStart + clangField->getBitWidthValue();
}
addOpaqueBitField(bitStart, bitEnd);
continue;
}
VarDecl *swiftField = nullptr;
if (sfi != sfe) {
swiftField = *sfi;
if (isImportOfClangField(swiftField, clangField)) {
++sfi;
} else {
swiftField = nullptr;
}
}
// Try to position this field. If this fails, it's because we
// didn't lay out padding correctly.
addStructField(clangField, swiftField, layout);
}
assert(sfi == sfe && "more Swift fields than there were Clang fields?");
auto objectSize = isBaseSubobject ? layout.getDataSize() : layout.getSize();
Size objectTotalStride = Size(objectSize.getQuantity());
// Unless this is a base subobject of a C++ type, we do not take advantage
// of tail padding, because that would prevent us from passing the address
// of the object off to C, which is a pretty likely scenario for imported C
// types.
// In C++, fields of a derived class might get placed into tail padding of a
// base class, in which case we should not add extra padding here.
assert(NextOffset <= SubobjectAdjustment + objectTotalStride);
assert(SpareBits.size() <= SubobjectAdjustment.getValueInBits() +
objectTotalStride.getValueInBits());
if (NextOffset < objectTotalStride) {
addPaddingField(objectTotalStride);
}
}
/// Place the next struct field at its appropriate offset.
void addStructField(const clang::FieldDecl *clangField,
VarDecl *swiftField, const clang::ASTRecordLayout &layout) {
bool isZeroSized = clangField->isZeroSize(ClangContext);
unsigned fieldOffset = layout.getFieldOffset(clangField->getFieldIndex());
assert(!clangField->isBitField());
Size offset( SubobjectAdjustment.getValue() + fieldOffset / 8);
std::optional<clang::CharUnits> dataSize;
if (clangField->hasAttr<clang::NoUniqueAddressAttr>()) {
if (const auto *rd = clangField->getType()->getAsRecordDecl()) {
// Clang can store the next field in the padding of this one.
const auto &fieldLayout = ClangContext.getASTRecordLayout(rd);
dataSize = fieldLayout.getDataSize();
}
}
// If we have a Swift import of this type, use our lowered information.
if (swiftField) {
auto &fieldTI = cast<FixedTypeInfo>(IGM.getTypeInfo(
SwiftType.getFieldType(swiftField, IGM.getSILModule(),
IGM.getMaximalTypeExpansionContext())));
addField(swiftField, offset, fieldTI, isZeroSized);
auto fieldTy =
swiftField->getInterfaceType()->lookThroughSingleOptionalType();
if (fieldTy->isAnyClassReferenceType() &&
fieldTy->getReferenceCounting() != ReferenceCounting::None)
hasReferenceField = true;
else if (auto structDecl = fieldTy->getStructOrBoundGenericStruct();
structDecl && structDecl->hasClangNode() &&
getStructTypeInfoKind(fieldTI) ==
StructTypeInfoKind::LoadableClangRecordTypeInfo)
if (fieldTI.as<LoadableClangRecordTypeInfo>().hasReferenceField())
hasReferenceField = true;
return;
}
// Otherwise, add it as an opaque blob.
auto fieldTypeSize = ClangContext.getTypeSizeInChars(clangField->getType());
auto fieldSize = isZeroSized ? clang::CharUnits::Zero()
: dataSize.value_or(fieldTypeSize);
return addOpaqueField(offset, Size(fieldSize.getQuantity()));
}
/// Add opaque storage for bitfields spanning the given range of bits.
void addOpaqueBitField(unsigned bitBegin, unsigned bitEnd) {
assert(bitBegin <= bitEnd);
// No need to add storage for zero-width bitfields.
if (bitBegin == bitEnd) return;
// Round up to an even number of bytes.
assert(bitBegin % 8 == 0);
Size offset = Size(bitBegin / 8);
Size byteLength = Size((bitEnd - bitBegin + 7) / 8);
addOpaqueField(offset, byteLength);
}
/// Add opaque storage at the given offset.
void addOpaqueField(Size offset, Size fieldSize) {
// No need to add storage for zero-size fields (e.g. incomplete array
// decls).
if (fieldSize.isZero()) return;
auto &opaqueTI = IGM.getOpaqueStorageTypeInfo(fieldSize, Alignment(1));
addField(nullptr, offset, opaqueTI, false);
}
/// Add storage for an (optional) Swift field at the given offset.
void addField(VarDecl *swiftField, Size offset,
const FixedTypeInfo &fieldType, bool isZeroSized) {
assert(isZeroSized || offset >= NextOffset && "adding fields out of order");
// Add a padding field if required.
if (!isZeroSized && offset != NextOffset)
addPaddingField(offset);
addFieldInfo(swiftField, fieldType, isZeroSized);
}
/// Add information to track a value field at the current offset.
void addFieldInfo(VarDecl *swiftField, const FixedTypeInfo &fieldType, bool isZeroSized) {
bool isLoadableField = isa<LoadableTypeInfo>(fieldType);
unsigned explosionSize = 0;
if (isLoadableField)
explosionSize = cast<LoadableTypeInfo>(fieldType).getExplosionSize();
unsigned explosionBegin = NextExplosionIndex;
NextExplosionIndex += explosionSize;
unsigned explosionEnd = NextExplosionIndex;
ElementLayout layout = ElementLayout::getIncomplete(fieldType);
auto isEmpty = isZeroSized || fieldType.isKnownEmpty(ResilienceExpansion::Maximal);
if (isEmpty) {
if (isZeroSized)
layout.completeEmpty(
fieldType.isTriviallyDestroyable(ResilienceExpansion::Maximal), NextOffset);
else
layout.completeEmptyTailAllocatedCType(
fieldType.isTriviallyDestroyable(ResilienceExpansion::Maximal), NextOffset);
} else
layout.completeFixed(fieldType.isTriviallyDestroyable(ResilienceExpansion::Maximal),
NextOffset, LLVMFields.size());
if (isLoadableField)
FieldInfos.push_back(
ClangFieldInfo(swiftField, layout, explosionBegin, explosionEnd));
else
FieldInfos.push_back(ClangFieldInfo(swiftField, layout, fieldType));
if (!isEmpty) {
LLVMFields.push_back(fieldType.getStorageType());
NextOffset += fieldType.getFixedSize();
SpareBits.append(fieldType.getSpareBits());
}
}
/// Add padding to get up to the given offset.
void addPaddingField(Size offset) {
assert(offset > NextOffset);
Size count = offset - NextOffset;
LLVMFields.push_back(llvm::ArrayType::get(IGM.Int8Ty, count.getValue()));
NextOffset = offset;
SpareBits.appendSetBits(count.getValueInBits());
}
};
} // end anonymous namespace
/// A convenient macro for delegating an operation to all of the
/// various struct implementations.
#define FOR_STRUCT_IMPL(IGF, type, op, ...) \
do { \
auto &structTI = IGF.getTypeInfo(type); \
switch (getStructTypeInfoKind(structTI)) { \
case StructTypeInfoKind::LoadableClangRecordTypeInfo: \
return structTI.as<LoadableClangRecordTypeInfo>().op(IGF, __VA_ARGS__); \
case StructTypeInfoKind::AddressOnlyClangRecordTypeInfo: \
return structTI.as<AddressOnlyCXXClangRecordTypeInfo>().op(IGF, \
__VA_ARGS__); \
case StructTypeInfoKind::LoadableStructTypeInfo: \
return structTI.as<LoadableStructTypeInfo>().op(IGF, __VA_ARGS__); \
case StructTypeInfoKind::FixedStructTypeInfo: \
return structTI.as<FixedStructTypeInfo>().op(IGF, __VA_ARGS__); \
case StructTypeInfoKind::NonFixedStructTypeInfo: \
return structTI.as<NonFixedStructTypeInfo>().op(IGF, __VA_ARGS__); \
case StructTypeInfoKind::ResilientStructTypeInfo: \
llvm_unreachable("resilient structs are opaque"); \
} \
llvm_unreachable("bad struct type info kind!"); \
} while (0)
Address irgen::projectPhysicalStructMemberAddress(IRGenFunction &IGF,
Address base,
SILType baseType,
VarDecl *field) {
FOR_STRUCT_IMPL(IGF, baseType, projectFieldAddress, base,
baseType, field);
}
void irgen::projectPhysicalStructMemberFromExplosion(IRGenFunction &IGF,
SILType baseType,
Explosion &base,
VarDecl *field,
Explosion &out) {
FOR_STRUCT_IMPL(IGF, baseType, projectFieldFromExplosion, base, field, out);
}
llvm::Constant *irgen::emitPhysicalStructMemberFixedOffset(IRGenModule &IGM,
SILType baseType,
VarDecl *field) {
FOR_STRUCT_IMPL(IGM, baseType, getConstantFieldOffset, field);
}
MemberAccessStrategy
irgen::getPhysicalStructMemberAccessStrategy(IRGenModule &IGM,
SILType baseType, VarDecl *field) {
FOR_STRUCT_IMPL(IGM, baseType, getFieldAccessStrategy, baseType, field);
}
std::optional<unsigned> irgen::getPhysicalStructFieldIndex(IRGenModule &IGM,
SILType baseType,
VarDecl *field) {
FOR_STRUCT_IMPL(IGM, baseType, getFieldIndexIfNotEmpty, field);
}
const TypeInfo *irgen::getPhysicalStructFieldTypeInfo(IRGenModule &IGM,
SILType baseType,
VarDecl *field) {
FOR_STRUCT_IMPL(IGM, baseType, getFieldTypeInfo, field);
}
void IRGenModule::emitStructDecl(StructDecl *st) {
if (!IRGen.hasLazyMetadata(st) &&
!st->getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
emitStructMetadata(*this, st);
emitFieldDescriptor(st);
}
emitNestedTypeDecls(st->getMembers());
}
void IRGenModule::maybeEmitOpaqueTypeDecl(OpaqueTypeDecl *opaque) {
if (opaque->getASTContext().LangOpts.hasFeature(Feature::Embedded))
return;
if (!opaque->isAvailableDuringLowering())
return;
if (IRGen.Opts.EnableAnonymousContextMangledNames) {
// If we're emitting anonymous context mangled names for debuggability,
// then emit all opaque type descriptors and make them runtime-discoverable
// so that remote ast/mirror can recover them.
addRuntimeResolvableType(opaque);
if (IRGen.hasLazyMetadata(opaque))
IRGen.noteUseOfOpaqueTypeDescriptor(opaque);
else {
if (IRGen.EmittedNonLazyOpaqueTypeDecls.insert(opaque).second)
emitOpaqueTypeDecl(opaque);
}
} else if (!IRGen.hasLazyMetadata(opaque)) {
if (IRGen.EmittedNonLazyOpaqueTypeDecls.insert(opaque).second)
emitOpaqueTypeDecl(opaque);
}
}
namespace {
/// A type implementation for resilient struct types. This is not a
/// StructTypeInfoBase at all, since we don't know anything about
/// the struct's fields.
class ResilientStructTypeInfo
: public ResilientTypeInfo<ResilientStructTypeInfo>
{
public:
ResilientStructTypeInfo(llvm::Type *T,
IsCopyable_t copyable,
IsABIAccessible_t abiAccessible)
: ResilientTypeInfo(T, copyable, abiAccessible) {
setSubclassKind((unsigned) StructTypeInfoKind::ResilientStructTypeInfo);
}
TypeLayoutEntry
*buildTypeLayoutEntry(IRGenModule &IGM,
SILType T,
bool useStructLayouts) const override {
return IGM.typeLayoutCache.getOrCreateResilientEntry(T);
}
};
} // end anonymous namespace
const TypeInfo *
TypeConverter::convertResilientStruct(IsCopyable_t copyable,
IsABIAccessible_t abiAccessible) {
llvm::Type *storageType = IGM.OpaqueTy;
return new ResilientStructTypeInfo(storageType, copyable, abiAccessible);
}
const TypeInfo *TypeConverter::convertStructType(TypeBase *key, CanType type,
StructDecl *D){
// All resilient structs have the same opaque lowering, since they are
// indistinguishable as values --- except that we have to track
// ABI-accessibility.
//
// Treat infinitely-sized types as resilient as well, since they can never
// be concretized.
if (IGM.isResilient(D, ResilienceExpansion::Maximal)
|| IGM.getSILTypes().getTypeProperties(SILType::getPrimitiveAddressType(type),
TypeExpansionContext::minimal())
.isInfinite()) {
auto copyable = !D->canBeCopyable()
? IsNotCopyable : IsCopyable;
auto structAccessible =
IsABIAccessible_t(IGM.getSILModule().isTypeMetadataAccessible(type));
auto *bitwiseCopyableProtocol =
IGM.getSwiftModule()->getASTContext().getProtocol(
KnownProtocolKind::BitwiseCopyable);
if (bitwiseCopyableProtocol &&
checkConformance(type, bitwiseCopyableProtocol)) {
return BitwiseCopyableTypeInfo::create(IGM.OpaqueTy, structAccessible);
}
return &getResilientStructTypeInfo(copyable, structAccessible);
}
// Create the struct type.
auto ty = IGM.createNominalType(type);
// Register a forward declaration before we look at any of the child types.
addForwardDecl(key);
// Use different rules for types imported from C.
if (D->hasClangNode()) {
const clang::Decl *clangDecl = D->getClangNode().getAsDecl();
assert(clangDecl && "Swift struct from an imported C macro?");
if (auto clangRecord = dyn_cast<clang::RecordDecl>(clangDecl)) {
ClangRecordLowering lowering(IGM, D, clangRecord,
SILType::getPrimitiveObjectType(type));
lowering.collectRecordFields();
return lowering.createTypeInfo(ty);
} else if (isa<clang::EnumDecl>(clangDecl)) {
// Fall back to Swift lowering for the enum's representation as a struct.
assert(D->getStoredProperties().size() == 1 &&
"Struct representation of a Clang enum should wrap one value");
} else if (clangDecl->hasAttr<clang::SwiftNewTypeAttr>()) {
// Fall back to Swift lowering for the underlying type's
// representation as a struct member.
assert(D->getStoredProperties().size() == 1 &&
"Struct representation of a swift_newtype should wrap one value");
} else {
llvm_unreachable("Swift struct represents unexpected imported type");
}
}
// Collect all the fields from the type.
SmallVector<VarDecl*, 8> fields;
for (VarDecl *VD : D->getStoredProperties())
fields.push_back(VD);
// Build the type.
StructTypeBuilder builder(IGM, ty, type);
return builder.layout(fields);
}