blob: 971662ddf66b1ce400028da45a59b56b2a3fd68a [file] [log] [blame]
//===--- SILGenType.cpp - SILGen for types and their members --------------===//
//
// 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 contains code for emitting code associated with types:
// - methods
// - ObjC dispatch thunks
// - SIL v-tables
// - etc.
//
//===----------------------------------------------------------------------===//
#include "SILGenFunction.h"
#include "Scope.h"
#include "ManagedValue.h"
#include "swift/AST/AST.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/TypeMemberVisitor.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/TypeLowering.h"
using namespace swift;
using namespace Lowering;
SILFunction *SILGenModule::getDynamicThunk(SILDeclRef constant,
SILConstantInfo constantInfo) {
// Mangle the constant with a _TTD header.
auto name = constant.mangle(SILDeclRef::ManglingKind::DynamicThunk);
IsFragile_t isFragile = IsNotFragile;
if (makeModuleFragile)
isFragile = IsFragile;
if (constant.isFragile())
isFragile = IsFragile;
auto F = M.getOrCreateFunction(constant.getDecl(), name, SILLinkage::Shared,
constantInfo.getSILType().castTo<SILFunctionType>(),
IsBare, IsTransparent, isFragile, IsThunk);
if (F->empty()) {
// Emit the thunk if we haven't yet.
// Currently a dynamic thunk looks just like a foreign-to-native thunk around
// an ObjC method. This would change if we introduced a native
// runtime-hookable mechanism.
SILGenFunction SGF(*this, *F);
SGF.emitForeignToNativeThunk(constant);
}
return F;
}
SILFunction *
SILGenModule::emitVTableMethod(SILDeclRef derived, SILDeclRef base,
SILLinkage &implLinkage) {
SILFunction *implFn = getFunction(derived, NotForDefinition);
implLinkage = implFn->getLinkage();
// As a fast path, if there is no override, definitely no thunk is necessary.
if (derived == base)
return implFn;
// Determine the derived thunk type by lowering the derived type against the
// abstraction pattern of the base.
auto baseInfo = Types.getConstantInfo(base);
auto derivedInfo = Types.getConstantInfo(derived);
auto basePattern = AbstractionPattern(baseInfo.LoweredInterfaceType);
auto overrideInfo = M.Types.getConstantOverrideInfo(derived, base);
// The override member type is semantically a subtype of the base
// member type. If the override is ABI compatible, we do not need
// a thunk.
if (overrideInfo == derivedInfo)
return implFn;
// Generate the thunk name.
// TODO: If we allocated a new vtable slot for the derived method, then
// further derived methods would potentially need multiple thunks, and we
// would need to mangle the base method into the symbol as well.
auto name = derived.mangle(SILDeclRef::ManglingKind::VTableMethod);
// If we already emitted this thunk, reuse it.
// TODO: Allocating new vtable slots for derived methods with different ABIs
// would invalidate the assumption that the same thunk is correct, as above.
if (auto existingThunk = M.lookUpFunction(name))
return existingThunk;
auto *derivedDecl = cast<AbstractFunctionDecl>(derived.getDecl());
SILLocation loc(derivedDecl);
auto thunk =
M.createFunction(SILLinkage::Private,
name, overrideInfo.SILFnType,
derivedDecl->getGenericEnvironment(), loc, IsBare,
IsNotTransparent, IsNotFragile);
thunk->setDebugScope(new (M) SILDebugScope(loc, thunk));
SILGenFunction(*this, *thunk)
.emitVTableThunk(derived, implFn, basePattern,
overrideInfo.LoweredInterfaceType,
derivedInfo.LoweredInterfaceType);
return thunk;
}
SILValue SILGenFunction::emitDynamicMethodRef(SILLocation loc,
SILDeclRef constant,
SILConstantInfo constantInfo) {
// If the method is foreign, its foreign thunk will handle the dynamic
// dispatch for us.
if (constant.isForeignToNativeThunk()) {
if (!SGM.hasFunction(constant))
SGM.emitForeignToNativeThunk(constant);
return B.createFunctionRef(loc, SGM.getFunction(constant, NotForDefinition));
}
// Otherwise, we need a dynamic dispatch thunk.
SILFunction *F = SGM.getDynamicThunk(constant, constantInfo);
return B.createFunctionRef(loc, F);
}
bool SILGenModule::requiresObjCMethodEntryPoint(FuncDecl *method) {
// Property accessors should be generated alongside the property unless
// the @NSManaged attribute is present.
if (method->isGetterOrSetter()) {
auto asd = method->getAccessorStorageDecl();
return asd->isObjC() && !asd->getAttrs().hasAttribute<NSManagedAttr>();
}
if (method->getAttrs().hasAttribute<NSManagedAttr>())
return false;
return method->isObjC() || method->getAttrs().hasAttribute<IBActionAttr>();
}
bool SILGenModule::requiresObjCMethodEntryPoint(ConstructorDecl *constructor) {
return constructor->isObjC();
}
namespace {
/// An ASTVisitor for populating SILVTable entries from ClassDecl members.
class SILGenVTable : public Lowering::ASTVisitor<SILGenVTable> {
public:
SILGenModule &SGM;
ClassDecl *theClass;
std::vector<SILVTable::Entry> vtableEntries;
SILGenVTable(SILGenModule &SGM, ClassDecl *theClass)
: SGM(SGM), theClass(theClass)
{
// Populate the superclass members, if any.
Type super = theClass->getSuperclass();
if (super && super->getClassOrBoundGenericClass())
visitAncestor(super->getClassOrBoundGenericClass());
}
~SILGenVTable() {
// Create the vtable.
SILVTable::create(SGM.M, theClass, vtableEntries);
}
void visitAncestor(ClassDecl *ancestor) {
// Recursively visit all our ancestors.
Type super = ancestor->getSuperclass();
if (super && super->getClassOrBoundGenericClass())
visitAncestor(super->getClassOrBoundGenericClass());
// Only visit the members for a class defined natively.
if (!ancestor->hasClangNode()) {
for (auto member : ancestor->getMembers())
visit(member);
}
}
// Add an entry to the vtable.
void addEntry(SILDeclRef member) {
/// Get the function to reference from the vtable.
auto getVtableEntry = [&](SILDeclRef entry) -> SILVTable::Entry {
// If the member is dynamic, reference its dynamic dispatch thunk so that
// it will be redispatched, funneling the method call through the runtime
// hook point.
// TODO: Dynamic thunks could conceivably require reabstraction too.
if (member.getDecl()->getAttrs().hasAttribute<DynamicAttr>())
return { entry,
SGM.getDynamicThunk(member, SGM.Types.getConstantInfo(member)),
SILLinkage::Public };
// The derived method may require thunking to match up to the ABI of the
// base method.
SILLinkage implLinkage;
SILFunction *implFn = SGM.emitVTableMethod(member, entry, implLinkage);
return { entry, implFn, stripExternalFromLinkage(implLinkage) };
};
// Try to find an overridden entry.
// NB: Mutates vtableEntries in-place
// FIXME: O(n^2)
if (auto overridden = member.getNextOverriddenVTableEntry()) {
for (SILVTable::Entry &entry : vtableEntries) {
SILDeclRef ref = overridden;
do {
// Replace the overridden member.
if (entry.Method == ref) {
// The entry is keyed by the least derived method.
entry = getVtableEntry(ref);
return;
}
} while ((ref = ref.getOverridden()));
}
llvm_unreachable("no overridden vtable entry?!");
}
// If this is a final member and isn't overriding something, we don't need
// to add it to the vtable.
if (member.getDecl()->isFinal())
return;
// If this is dynamic and isn't overriding a non-dynamic method, it'll
// always be accessed by objc_msgSend, so we don't need to add it to the
// vtable.
if (member.getDecl()->getAttrs().hasAttribute<DynamicAttr>())
return;
// Otherwise, introduce a new vtable entry.
vtableEntries.push_back(getVtableEntry(member));
}
// Default for members that don't require vtable entries.
void visitDecl(Decl*) {}
void visitFuncDecl(FuncDecl *fd) {
// ObjC decls don't go in vtables.
if (fd->hasClangNode())
return;
// Observers don't get separate vtable entries.
if (fd->isObservingAccessor())
return;
addEntry(SILDeclRef(fd));
}
void visitConstructorDecl(ConstructorDecl *cd) {
// Stub constructors don't get an entry, unless they were synthesized to
// override a non-required designated initializer in the superclass.
if (cd->hasStubImplementation() && !cd->getOverriddenDecl())
return;
// Required constructors (or overrides thereof) have their allocating entry
// point in the vtable.
bool isRequired = false;
auto override = cd;
while (override) {
if (override->isRequired()) {
isRequired = true;
break;
}
override = override->getOverriddenDecl();
}
if (isRequired) {
addEntry(SILDeclRef(cd, SILDeclRef::Kind::Allocator));
}
// All constructors have their initializing constructor in the
// vtable, which can be used by a convenience initializer.
addEntry(SILDeclRef(cd, SILDeclRef::Kind::Initializer));
}
void visitVarDecl(VarDecl *vd) {
// Note: dynamically-dispatched properties have their getter and setter
// added to the vtable when they are visited.
}
void visitDestructorDecl(DestructorDecl *dd) {
if (dd->getParent()->getAsClassOrClassExtensionContext() == theClass) {
// Add the deallocating destructor to the vtable just for the purpose
// that it is referenced and cannot be eliminated by dead function removal.
// In reality, the deallocating destructor is referenced directly from
// the HeapMetadata for the class.
addEntry(SILDeclRef(dd, SILDeclRef::Kind::Deallocator));
if (SGM.requiresIVarDestroyer(theClass))
addEntry(SILDeclRef(theClass, SILDeclRef::Kind::IVarDestroyer));
}
}
void visitSubscriptDecl(SubscriptDecl *sd) {
// Note: dynamically-dispatched properties have their getter and setter
// added to the vtable when they are visited.
}
};
static void emitTypeMemberGlobalVariable(SILGenModule &SGM,
VarDecl *var) {
if (var->getDeclContext()->isGenericContext()) {
assert(var->getDeclContext()->getGenericSignatureOfContext()
->areAllParamsConcrete()
&& "generic static vars are not implemented yet");
}
if (var->getDeclContext()->getAsClassOrClassExtensionContext()) {
assert(var->isFinal() && "only 'static' ('class final') stored properties are implemented in classes");
}
SGM.addGlobalVariable(var);
}
/// An ASTVisitor for generating SIL from method declarations
/// inside nominal types.
class SILGenType : public TypeMemberVisitor<SILGenType> {
public:
SILGenModule &SGM;
NominalTypeDecl *theType;
Optional<SILGenVTable> genVTable;
SILGenType(SILGenModule &SGM, NominalTypeDecl *theType)
: SGM(SGM), theType(theType) {}
/// Emit SIL functions for all the members of the type.
void emitType() {
// Start building a vtable if this is a class.
if (auto theClass = dyn_cast<ClassDecl>(theType))
genVTable.emplace(SGM, theClass);
for (Decl *member : theType->getMembers()) {
if (genVTable)
genVTable->visit(member);
visit(member);
}
if (auto protocol = dyn_cast<ProtocolDecl>(theType)) {
if (!protocol->isObjC())
SGM.emitDefaultWitnessTable(protocol);
return;
}
// Emit witness tables for conformances of concrete types. Protocol types
// are existential and do not have witness tables.
for (auto *conformance : theType->getLocalConformances(
ConformanceLookupKind::All,
nullptr, /*sorted=*/true)) {
if (conformance->isComplete() &&
isa<NormalProtocolConformance>(conformance))
SGM.getWitnessTable(conformance);
}
}
//===--------------------------------------------------------------------===//
// Visitors for subdeclarations
//===--------------------------------------------------------------------===//
void visitTypeAliasDecl(TypeAliasDecl *tad) {}
void visitAbstractTypeParamDecl(AbstractTypeParamDecl *tpd) {}
void visitModuleDecl(ModuleDecl *md) {}
void visitNominalTypeDecl(NominalTypeDecl *ntd) {
SILGenType(SGM, ntd).emitType();
}
void visitFuncDecl(FuncDecl *fd) {
ProfilerRAII Profiler(SGM, fd);
SGM.emitFunction(fd);
// FIXME: Default implementations in protocols.
if (SGM.requiresObjCMethodEntryPoint(fd) &&
!isa<ProtocolDecl>(fd->getDeclContext()))
SGM.emitObjCMethodThunk(fd);
}
void visitConstructorDecl(ConstructorDecl *cd) {
ProfilerRAII Profiler(SGM, cd);
SGM.emitConstructor(cd);
if (SGM.requiresObjCMethodEntryPoint(cd) &&
!isa<ProtocolDecl>(cd->getDeclContext()))
SGM.emitObjCConstructorThunk(cd);
}
void visitDestructorDecl(DestructorDecl *dd) {
assert(isa<ClassDecl>(theType) && "destructor in a non-class type");
ProfilerRAII Profiler(SGM, dd);
SGM.emitDestructor(cast<ClassDecl>(theType), dd);
}
void visitEnumCaseDecl(EnumCaseDecl *ecd) {}
void visitEnumElementDecl(EnumElementDecl *ued) {}
void visitPatternBindingDecl(PatternBindingDecl *pd) {
// Emit initializers.
for (unsigned i = 0, e = pd->getNumPatternEntries(); i != e; ++i) {
if (pd->getInit(i)) {
if (pd->isStatic())
SGM.emitGlobalInitialization(pd, i);
else
SGM.emitStoredPropertyInitialization(pd, i);
}
}
}
void visitVarDecl(VarDecl *vd) {
if (vd->hasBehavior())
SGM.emitPropertyBehavior(vd);
// Collect global variables for static properties.
// FIXME: We can't statically emit a global variable for generic properties.
if (vd->isStatic() && vd->hasStorage()) {
return emitTypeMemberGlobalVariable(SGM, vd);
}
visitAbstractStorageDecl(vd);
}
void visitAbstractStorageDecl(AbstractStorageDecl *asd) {
// FIXME: Default implementations in protocols.
if (asd->isObjC() && !isa<ProtocolDecl>(asd->getDeclContext()))
SGM.emitObjCPropertyMethodThunks(asd);
}
};
} // end anonymous namespace
void SILGenModule::visitNominalTypeDecl(NominalTypeDecl *ntd) {
SILGenType(*this, ntd).emitType();
}
/// SILGenExtension - an ASTVisitor for generating SIL from method declarations
/// and protocol conformances inside type extensions.
class SILGenExtension : public TypeMemberVisitor<SILGenExtension> {
public:
SILGenModule &SGM;
SILGenExtension(SILGenModule &SGM)
: SGM(SGM) {}
/// Emit SIL functions for all the members of the extension.
void emitExtension(ExtensionDecl *e) {
for (Decl *member : e->getMembers())
visit(member);
if (!e->getExtendedType()->isExistentialType()) {
// Emit witness tables for protocol conformances introduced by the
// extension.
for (auto *conformance : e->getLocalConformances(
ConformanceLookupKind::All,
nullptr, /*sorted=*/true)) {
if (conformance->isComplete() &&
isa<NormalProtocolConformance>(conformance))
SGM.getWitnessTable(conformance);
}
}
}
//===--------------------------------------------------------------------===//
// Visitors for subdeclarations
//===--------------------------------------------------------------------===//
void visitTypeAliasDecl(TypeAliasDecl *tad) {}
void visitAbstractTypeParamDecl(AbstractTypeParamDecl *tpd) {}
void visitModuleDecl(ModuleDecl *md) {}
void visitNominalTypeDecl(NominalTypeDecl *ntd) {
SILGenType(SGM, ntd).emitType();
}
void visitFuncDecl(FuncDecl *fd) {
ProfilerRAII Profiler(SGM, fd);
SGM.emitFunction(fd);
if (SGM.requiresObjCMethodEntryPoint(fd))
SGM.emitObjCMethodThunk(fd);
}
void visitConstructorDecl(ConstructorDecl *cd) {
ProfilerRAII Profiler(SGM, cd);
SGM.emitConstructor(cd);
if (SGM.requiresObjCMethodEntryPoint(cd))
SGM.emitObjCConstructorThunk(cd);
}
void visitDestructorDecl(DestructorDecl *dd) {
llvm_unreachable("destructor in extension?!");
}
void visitPatternBindingDecl(PatternBindingDecl *pd) {
// Emit initializers for static variables.
for (unsigned i = 0, e = pd->getNumPatternEntries(); i != e; ++i) {
if (pd->getInit(i)) {
assert(pd->isStatic() && "stored property in extension?!");
SGM.emitGlobalInitialization(pd, i);
}
}
}
void visitVarDecl(VarDecl *vd) {
if (vd->hasBehavior())
SGM.emitPropertyBehavior(vd);
if (vd->hasStorage()) {
assert(vd->isStatic() && "stored property in extension?!");
return emitTypeMemberGlobalVariable(SGM, vd);
}
visitAbstractStorageDecl(vd);
}
void visitEnumCaseDecl(EnumCaseDecl *ecd) {}
void visitEnumElementDecl(EnumElementDecl *ed) {
llvm_unreachable("enum elements aren't allowed in extensions");
}
void visitAbstractStorageDecl(AbstractStorageDecl *vd) {
if (vd->isObjC())
SGM.emitObjCPropertyMethodThunks(vd);
}
};
void SILGenModule::visitExtensionDecl(ExtensionDecl *ed) {
SILGenExtension(*this).emitExtension(ed);
}