Files
swift-mirror/include/swift/SIL/SILVTableVisitor.h
Doug Gregor ae4fbb6716 [AST] Generalize and rename ClassDecl::getEmittedMembers()
Generalize `ClassDecl::getEmittedMembers()` to operate on an
`IterableDeclContext`, so that it can be for other nominal types,
extensions, etc. Rename to `getSemanticMembers()` to indicate that
these are all of the members that are semantically part of that
context.

Clean up the implementation slightly so it only forces type checking
for the conformances within that particular context (using
`getLocalConformances()`) and doesn't need to list out each of the
protocols it cares about.
2020-09-01 13:01:10 -07:00

159 lines
5.7 KiB
C++

//===--- SILVTableVisitor.h - Class vtable visitor --------------*- C++ -*-===//
//
// 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 defines the SILVTableVisitor class, which is used to generate and
// perform lookups in class method vtables.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SIL_SILVTABLEVISITOR_H
#define SWIFT_SIL_SILVTABLEVISITOR_H
#include "swift/AST/Decl.h"
#include "swift/AST/Types.h"
namespace swift {
/// A CRTP class for visiting virtually-dispatched methods of a class.
///
/// You must override these two methods in your subclass:
///
/// - addMethod(SILDeclRef):
/// introduce a new vtable entry
///
/// - addMethodOverride(SILDeclRef baseRef, SILDeclRef derivedRef):
/// update vtable entry for baseRef to call derivedRef
///
/// - addPlaceholder(MissingMemberDecl *);
/// introduce an entry for a method that could not be deserialized
///
template <class T> class SILVTableVisitor {
T &asDerived() { return *static_cast<T*>(this); }
void maybeAddMethod(FuncDecl *fd) {
assert(!fd->hasClangNode());
SILDeclRef constant(fd, SILDeclRef::Kind::Func);
maybeAddEntry(constant);
for (auto *diffAttr : fd->getAttrs().getAttributes<DifferentiableAttr>()) {
auto jvpConstant = constant.asAutoDiffDerivativeFunction(
AutoDiffDerivativeFunctionIdentifier::get(
AutoDiffDerivativeFunctionKind::JVP,
diffAttr->getParameterIndices(),
diffAttr->getDerivativeGenericSignature(), fd->getASTContext()));
maybeAddEntry(jvpConstant);
auto vjpConstant = constant.asAutoDiffDerivativeFunction(
AutoDiffDerivativeFunctionIdentifier::get(
AutoDiffDerivativeFunctionKind::VJP,
diffAttr->getParameterIndices(),
diffAttr->getDerivativeGenericSignature(), fd->getASTContext()));
maybeAddEntry(vjpConstant);
}
}
void maybeAddConstructor(ConstructorDecl *cd) {
assert(!cd->hasClangNode());
// The allocating entry point is what is used for dynamic dispatch.
// The initializing entry point for designated initializers is only
// necessary for super.init chaining, which is sufficiently constrained
// to never need dynamic dispatch.
SILDeclRef constant(cd, SILDeclRef::Kind::Allocator);
maybeAddEntry(constant);
for (auto *diffAttr : cd->getAttrs().getAttributes<DifferentiableAttr>()) {
auto jvpConstant = constant.asAutoDiffDerivativeFunction(
AutoDiffDerivativeFunctionIdentifier::get(
AutoDiffDerivativeFunctionKind::JVP,
diffAttr->getParameterIndices(),
diffAttr->getDerivativeGenericSignature(), cd->getASTContext()));
maybeAddEntry(jvpConstant);
auto vjpConstant = constant.asAutoDiffDerivativeFunction(
AutoDiffDerivativeFunctionIdentifier::get(
AutoDiffDerivativeFunctionKind::VJP,
diffAttr->getParameterIndices(),
diffAttr->getDerivativeGenericSignature(), cd->getASTContext()));
maybeAddEntry(vjpConstant);
}
}
void maybeAddAccessors(AbstractStorageDecl *asd) {
asd->visitOpaqueAccessors([&](AccessorDecl *accessor) {
maybeAddMethod(accessor);
});
}
void maybeAddEntry(SILDeclRef declRef) {
// Introduce a new entry if required.
if (declRef.requiresNewVTableEntry())
asDerived().addMethod(declRef);
// Update any existing entries that it overrides.
auto nextRef = declRef;
while ((nextRef = nextRef.getNextOverriddenVTableEntry())) {
auto baseRef = nextRef.getOverriddenVTableEntry();
// If A.f() is overridden by B.f() which is overridden by
// C.f(), it's possible that C.f() is not visible from C.
// In this case, we pretend that B.f() is the least derived
// method with a vtable entry in the override chain.
//
// This works because we detect the possibility of this
// happening when we emit B.f() and do two things:
// - B.f() always gets a new vtable entry, even if it is
// ABI compatible with A.f()
// - The vtable thunk for the override of A.f() in B does a
// vtable dispatch to the implementation of B.f() for the
// concrete subclass, so a subclass of B only needs to
// replace the vtable entry for B.f(); a call to A.f()
// will correctly dispatch to the implementation of B.f()
// in the subclass.
if (!baseRef.getDecl()->isAccessibleFrom(
declRef.getDecl()->getDeclContext()))
break;
asDerived().addMethodOverride(baseRef, declRef);
nextRef = baseRef;
}
}
void maybeAddMember(Decl *member) {
if (isa<AccessorDecl>(member))
/* handled as part of its storage */;
else if (auto *fd = dyn_cast<FuncDecl>(member))
maybeAddMethod(fd);
else if (auto *cd = dyn_cast<ConstructorDecl>(member))
maybeAddConstructor(cd);
else if (auto *asd = dyn_cast<AbstractStorageDecl>(member))
maybeAddAccessors(asd);
else if (auto *placeholder = dyn_cast<MissingMemberDecl>(member))
asDerived().addPlaceholder(placeholder);
}
protected:
void addVTableEntries(ClassDecl *theClass) {
// Imported classes do not have a vtable.
if (!theClass->hasKnownSwiftImplementation())
return;
for (auto member : theClass->getSemanticMembers())
maybeAddMember(member);
}
};
}
#endif