Skip to content

Commit 6de9b1e

Browse files
authored
Merge pull request #9309 from jrose-apple/ast-vtables
Lift the decision of whether a method needs a vtable slot up to AST.
2 parents 0a54806 + 6c09803 commit 6de9b1e

File tree

16 files changed

+419
-338
lines changed

16 files changed

+419
-338
lines changed

include/swift/AST/Decl.h

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,14 @@ class alignas(1 << DeclAlignInBits) Decl {
365365

366366
/// Whether the function body throws.
367367
unsigned Throws : 1;
368+
369+
/// Whether this function requires a new vtable entry.
370+
unsigned NeedsNewVTableEntry : 1;
371+
372+
/// Whether NeedsNewVTableEntry is valid.
373+
unsigned HasComputedNeedsNewVTableEntry : 1;
368374
};
369-
enum { NumAbstractFunctionDeclBits = NumValueDeclBits + 11 };
375+
enum { NumAbstractFunctionDeclBits = NumValueDeclBits + 13 };
370376
static_assert(NumAbstractFunctionDeclBits <= 32, "fits in an unsigned");
371377

372378
class FuncDeclBitfields {
@@ -378,15 +384,8 @@ class alignas(1 << DeclAlignInBits) Decl {
378384

379385
/// \brief Whether 'static' or 'class' was used.
380386
unsigned StaticSpelling : 2;
381-
382-
/// Whether this function is a 'mutating' method.
383-
unsigned Mutating : 1;
384-
385-
/// Whether this function has a dynamic Self return type.
386-
unsigned HasDynamicSelf : 1;
387-
388387
};
389-
enum { NumFuncDeclBits = NumAbstractFunctionDeclBits + 5 };
388+
enum { NumFuncDeclBits = NumAbstractFunctionDeclBits + 3 };
390389
static_assert(NumFuncDeclBits <= 32, "fits in an unsigned");
391390

392391
class ConstructorDeclBitfields {
@@ -399,17 +398,8 @@ class alignas(1 << DeclAlignInBits) Decl {
399398
/// of the definition of the constructor that is useful only to semantic
400399
/// analysis and SIL generation.
401400
unsigned ComputedBodyInitKind : 3;
402-
403-
/// Whether this initializer is a stub placed into a subclass to
404-
/// catch invalid delegations to a designated initializer not
405-
/// overridden by the subclass. A stub will always trap at runtime.
406-
///
407-
/// Initializer stubs can be invoked from Objective-C or through
408-
/// the Objective-C runtime; there is no way to directly express
409-
/// an object construction that will invoke a stub.
410-
unsigned HasStubImplementation : 1;
411401
};
412-
enum { NumConstructorDeclBits = NumAbstractFunctionDeclBits + 4 };
402+
enum { NumConstructorDeclBits = NumAbstractFunctionDeclBits + 3 };
413403
static_assert(NumConstructorDeclBits <= 32, "fits in an unsigned");
414404

415405
class TypeDeclBitfields {
@@ -4790,6 +4780,8 @@ class AbstractFunctionDecl : public ValueDecl, public GenericContext {
47904780
AbstractFunctionDeclBits.NumParameterLists = NumParameterLists;
47914781
AbstractFunctionDeclBits.Overridden = false;
47924782
AbstractFunctionDeclBits.Throws = Throws;
4783+
AbstractFunctionDeclBits.NeedsNewVTableEntry = false;
4784+
AbstractFunctionDeclBits.HasComputedNeedsNewVTableEntry = false;
47934785

47944786
// Verify no bitfield truncation.
47954787
assert(AbstractFunctionDeclBits.NumParameterLists == NumParameterLists);
@@ -4903,6 +4895,21 @@ class AbstractFunctionDecl : public ValueDecl, public GenericContext {
49034895
return getBodyKind() == BodyKind::MemberwiseInitializer;
49044896
}
49054897

4898+
void setNeedsNewVTableEntry(bool value) {
4899+
AbstractFunctionDeclBits.HasComputedNeedsNewVTableEntry = true;
4900+
AbstractFunctionDeclBits.NeedsNewVTableEntry = value;
4901+
}
4902+
4903+
bool needsNewVTableEntry() const {
4904+
if (!AbstractFunctionDeclBits.HasComputedNeedsNewVTableEntry)
4905+
const_cast<AbstractFunctionDecl *>(this)->computeNeedsNewVTableEntry();
4906+
return AbstractFunctionDeclBits.NeedsNewVTableEntry;
4907+
}
4908+
4909+
private:
4910+
void computeNeedsNewVTableEntry();
4911+
4912+
public:
49064913
/// Retrieve the source range of the function body.
49074914
SourceRange getBodySourceRange() const;
49084915

@@ -5059,6 +5066,12 @@ class FuncDecl final : public AbstractFunctionDecl,
50595066
/// Whether we are statically dispatched even if overridable
50605067
unsigned ForcedStaticDispatch : 1;
50615068

5069+
/// Whether this function has a dynamic Self return type.
5070+
unsigned HasDynamicSelf : 1;
5071+
5072+
/// Whether this function is a 'mutating' method.
5073+
unsigned Mutating : 1;
5074+
50625075
/// \brief If this FuncDecl is an accessor for a property, this indicates
50635076
/// which property and what kind of accessor.
50645077
llvm::PointerIntPair<AbstractStorageDecl*, 3, AccessorKind> AccessorDecl;
@@ -5086,9 +5099,9 @@ class FuncDecl final : public AbstractFunctionDecl,
50865099
StaticLoc.isValid() || StaticSpelling != StaticSpellingKind::None;
50875100
FuncDeclBits.StaticSpelling = static_cast<unsigned>(StaticSpelling);
50885101
assert(NumParameterLists > 0 && "Must have at least an empty tuple arg");
5089-
FuncDeclBits.Mutating = false;
5090-
FuncDeclBits.HasDynamicSelf = false;
50915102

5103+
Mutating = false;
5104+
HasDynamicSelf = false;
50925105
ForcedStaticDispatch = false;
50935106
HaveSearchedForCommonOverloadReturnType = false;
50945107
HaveFoundCommonOverloadReturnType = false;
@@ -5141,10 +5154,10 @@ class FuncDecl final : public AbstractFunctionDecl,
51415154
FuncDeclBits.IsStatic = IsStatic;
51425155
}
51435156
bool isMutating() const {
5144-
return FuncDeclBits.Mutating;
5157+
return Mutating;
51455158
}
5146-
void setMutating(bool Mutating = true) {
5147-
FuncDeclBits.Mutating = Mutating;
5159+
void setMutating(bool mutating = true) {
5160+
Mutating = mutating;
51485161
}
51495162

51505163
/// \brief Returns the parameter lists(s) for the function definition.
@@ -5266,11 +5279,11 @@ class FuncDecl final : public AbstractFunctionDecl,
52665279

52675280
/// Determine whether this function has a dynamic \c Self return
52685281
/// type.
5269-
bool hasDynamicSelf() const { return FuncDeclBits.HasDynamicSelf; }
5282+
bool hasDynamicSelf() const { return HasDynamicSelf; }
52705283

52715284
/// Set whether this function has a dynamic \c Self return or not.
52725285
void setDynamicSelf(bool hasDynamicSelf) {
5273-
FuncDeclBits.HasDynamicSelf = hasDynamicSelf;
5286+
HasDynamicSelf = hasDynamicSelf;
52745287
}
52755288

52765289
void getLocalCaptures(SmallVectorImpl<CapturedValue> &Result) const {
@@ -5549,6 +5562,15 @@ class ConstructorDecl : public AbstractFunctionDecl {
55495562
/// The failability of this initializer, which is an OptionalTypeKind.
55505563
unsigned Failability : 2;
55515564

5565+
/// Whether this initializer is a stub placed into a subclass to
5566+
/// catch invalid delegations to a designated initializer not
5567+
/// overridden by the subclass. A stub will always trap at runtime.
5568+
///
5569+
/// Initializer stubs can be invoked from Objective-C or through
5570+
/// the Objective-C runtime; there is no way to directly express
5571+
/// an object construction that will invoke a stub.
5572+
unsigned HasStubImplementation : 1;
5573+
55525574
/// The location of the '!' or '?' for a failable initializer.
55535575
SourceLoc FailabilityLoc;
55545576

@@ -5708,13 +5730,13 @@ class ConstructorDecl : public AbstractFunctionDecl {
57085730

57095731
/// Whether the implementation of this method is a stub that traps at runtime.
57105732
bool hasStubImplementation() const {
5711-
return ConstructorDeclBits.HasStubImplementation;
5733+
return HasStubImplementation;
57125734
}
57135735

57145736
/// Set whether the implementation of this method is a stub that
57155737
/// traps at runtime.
57165738
void setStubImplementation(bool stub) {
5717-
ConstructorDeclBits.HasStubImplementation = stub;
5739+
HasStubImplementation = stub;
57185740
}
57195741

57205742
ConstructorDecl *getOverriddenDecl() const { return OverriddenDecl; }

include/swift/AST/Types.h

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -221,19 +221,22 @@ enum class TypeTraitResult {
221221

222222
/// Specifies which normally-unsafe type mismatches should be accepted when
223223
/// checking overrides.
224-
enum class OverrideMatchMode {
225-
/// Only accept overrides that are properly covariant.
226-
Strict,
224+
enum class TypeMatchFlags {
225+
/// Allow properly-covariant overrides.
226+
AllowOverride = 1 << 0,
227227
/// Allow a parameter with IUO type to be overridden by a parameter with non-
228228
/// optional type.
229-
AllowNonOptionalForIUOParam,
229+
AllowNonOptionalForIUOParam = 1 << 1,
230230
/// Allow any mismatches of Optional or ImplicitlyUnwrappedOptional at the
231231
/// top level of a type.
232232
///
233233
/// This includes function parameters and result types as well as tuple
234234
/// elements, but excludes generic parameters.
235-
AllowTopLevelOptionalMismatch
235+
AllowTopLevelOptionalMismatch = 1 << 2,
236+
/// Allow any ABI-compatible types to be considered matching.
237+
AllowABICompatible = 1 << 3,
236238
};
239+
using TypeMatchOptions = OptionSet<TypeMatchFlags>;
237240

238241
/// TypeBase - Base class for all types in Swift.
239242
class alignas(1 << TypeAlignInBits) TypeBase {
@@ -702,10 +705,9 @@ class alignas(1 << TypeAlignInBits) TypeBase {
702705
/// concrete types to form the argument type.
703706
bool isBindableTo(Type ty);
704707

705-
/// \brief Determines whether this type is permitted as a method override
706-
/// of the \p other.
707-
bool canOverride(Type other, OverrideMatchMode matchMode,
708-
LazyResolver *resolver);
708+
/// \brief Determines whether this type is similar to \p other as defined by
709+
/// \p matchOptions.
710+
bool matches(Type other, TypeMatchOptions matchOptions, LazyResolver *resolver);
709711

710712
/// \brief Determines whether this type has a retainable pointer
711713
/// representation, i.e. whether it is representable as a single,

include/swift/SIL/SILVTableVisitor.h

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,38 +43,33 @@ template <class T> class SILVTableVisitor {
4343
void maybeAddMethod(FuncDecl *fd) {
4444
assert(!fd->hasClangNode());
4545

46-
// Observing accessors and addressors don't get vtable entries.
47-
if (fd->isObservingAccessor() ||
48-
fd->getAddressorKind() != AddressorKind::NotAddressor)
49-
return;
50-
51-
maybeAddEntry(SILDeclRef(fd, SILDeclRef::Kind::Func));
46+
maybeAddEntry(SILDeclRef(fd, SILDeclRef::Kind::Func),
47+
fd->needsNewVTableEntry());
5248
}
5349

5450
void maybeAddConstructor(ConstructorDecl *cd) {
5551
assert(!cd->hasClangNode());
5652

57-
SILDeclRef initRef(cd, SILDeclRef::Kind::Initializer);
58-
59-
// Stub constructors don't get a vtable entry unless they were synthesized
60-
// to override a base class initializer.
61-
if (cd->hasStubImplementation() &&
62-
!initRef.getNextOverriddenVTableEntry())
63-
return;
64-
6553
// Required constructors (or overrides thereof) have their allocating entry
6654
// point in the vtable.
67-
if (cd->isRequired())
68-
maybeAddEntry(SILDeclRef(cd, SILDeclRef::Kind::Allocator));
55+
if (cd->isRequired()) {
56+
bool needsAllocatingEntry = cd->needsNewVTableEntry();
57+
if (!needsAllocatingEntry)
58+
if (auto *baseCD = cd->getOverriddenDecl())
59+
needsAllocatingEntry = !baseCD->isRequired();
60+
maybeAddEntry(SILDeclRef(cd, SILDeclRef::Kind::Allocator),
61+
needsAllocatingEntry);
62+
}
6963

7064
// All constructors have their initializing constructor in the
7165
// vtable, which can be used by a convenience initializer.
72-
maybeAddEntry(SILDeclRef(cd, SILDeclRef::Kind::Initializer));
66+
maybeAddEntry(SILDeclRef(cd, SILDeclRef::Kind::Initializer),
67+
cd->needsNewVTableEntry());
7368
}
7469

75-
void maybeAddEntry(SILDeclRef declRef) {
70+
void maybeAddEntry(SILDeclRef declRef, bool needsNewEntry) {
7671
// Introduce a new entry if required.
77-
if (Types.requiresNewVTableEntry(declRef))
72+
if (needsNewEntry)
7873
asDerived().addMethod(declRef);
7974

8075
// Update any existing entries that it overrides.

include/swift/SIL/TypeLowering.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -511,8 +511,6 @@ class TypeConverter {
511511

512512
llvm::DenseMap<OverrideKey, SILConstantInfo> ConstantOverrideTypes;
513513

514-
llvm::DenseMap<SILDeclRef, bool> RequiresVTableEntry;
515-
516514
llvm::DenseMap<AnyFunctionRef, CaptureInfo> LoweredCaptures;
517515

518516
CanAnyFunctionType makeConstantInterfaceType(SILDeclRef constant);
@@ -665,11 +663,6 @@ class TypeConverter {
665663
/// `constant` must refer to a method.
666664
SILParameterInfo getConstantSelfParameter(SILDeclRef constant);
667665

668-
/// Return if this method introduces a new vtable entry. This will be true
669-
/// if the method does not override any method of its base class, or if it
670-
/// overrides a method but has a more general AST type.
671-
bool requiresNewVTableEntry(SILDeclRef method);
672-
673666
/// Return the most derived override which requires a new vtable entry.
674667
/// If the method does not override anything or no override is vtable
675668
/// dispatched, will return the least derived method.
@@ -852,8 +845,6 @@ class TypeConverter {
852845
CanAnyFunctionType getBridgedFunctionType(AbstractionPattern fnPattern,
853846
CanAnyFunctionType fnType,
854847
AnyFunctionType::ExtInfo extInfo);
855-
856-
bool requiresNewVTableEntryUncached(SILDeclRef method);
857848
};
858849

859850
inline const TypeLowering &

include/swift/Serialization/ModuleFormat.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const uint16_t VERSION_MAJOR = 0;
5454
/// in source control, you should also update the comment to briefly
5555
/// describe what change you made. The content of this comment isn't important;
5656
/// it just ensures a conflict if two people change the module format.
57-
const uint16_t VERSION_MINOR = 342; // Last change: keypath instruction
57+
const uint16_t VERSION_MINOR = 343; // Last change: new vtable entry flag
5858

5959
using DeclID = PointerEmbeddedInt<unsigned, 31>;
6060
using DeclIDField = BCFixed<31>;
@@ -858,6 +858,7 @@ namespace decls_block {
858858
TypeIDField, // canonical interface type
859859
DeclIDField, // overridden decl
860860
AccessibilityKindField, // accessibility
861+
BCFixed<1>, // requires a new vtable slot
861862
BCArray<IdentifierIDField> // argument names
862863
// Trailed by its generic parameters, if any, followed by the parameter
863864
// patterns.
@@ -916,6 +917,7 @@ namespace decls_block {
916917
BCFixed<1>, // name is compound?
917918
AddressorKindField, // addressor kind
918919
AccessibilityKindField, // accessibility
920+
BCFixed<1>, // requires a new vtable slot
919921
BCArray<IdentifierIDField> // name components
920922
// The record is trailed by:
921923
// - its _silgen_name, if any

lib/AST/Decl.cpp

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4608,6 +4608,63 @@ AbstractFunctionDecl *AbstractFunctionDecl::getOverriddenDecl() const {
46084608
return nullptr;
46094609
}
46104610

4611+
static bool requiresNewVTableEntry(const AbstractFunctionDecl *decl) {
4612+
assert(isa<FuncDecl>(decl) || isa<ConstructorDecl>(decl));
4613+
4614+
// Final members are always be called directly.
4615+
// Dynamic methods are always accessed by objc_msgSend().
4616+
if (decl->isFinal() || decl->isDynamic())
4617+
return false;
4618+
4619+
if (auto *fd = dyn_cast<FuncDecl>(decl)) {
4620+
switch (fd->getAccessorKind()) {
4621+
case AccessorKind::NotAccessor:
4622+
case AccessorKind::IsGetter:
4623+
case AccessorKind::IsSetter:
4624+
break;
4625+
case AccessorKind::IsAddressor:
4626+
case AccessorKind::IsMutableAddressor:
4627+
return false;
4628+
case AccessorKind::IsMaterializeForSet:
4629+
// Special case -- materializeForSet on dynamic storage is not
4630+
// itself dynamic, but should be treated as such for the
4631+
// purpose of constructing a vtable.
4632+
// FIXME: It should probably just be 'final'.
4633+
if (fd->getAccessorStorageDecl()->isDynamic())
4634+
return false;
4635+
break;
4636+
case AccessorKind::IsWillSet:
4637+
case AccessorKind::IsDidSet:
4638+
return false;
4639+
}
4640+
}
4641+
4642+
auto base = decl->getOverriddenDecl();
4643+
if (!base || base->hasClangNode())
4644+
return true;
4645+
4646+
// If the method overrides something, we only need a new entry if the
4647+
// override has a more general AST type. However an abstraction
4648+
// change is OK; we don't want to add a whole new vtable entry just
4649+
// because an @in parameter because @owned, or whatever.
4650+
auto baseInterfaceTy = base->getInterfaceType();
4651+
auto derivedInterfaceTy = decl->getInterfaceType();
4652+
4653+
auto selfInterfaceTy = decl->getDeclContext()->getDeclaredInterfaceType();
4654+
4655+
auto overrideInterfaceTy = selfInterfaceTy->adjustSuperclassMemberDeclType(
4656+
base, decl, baseInterfaceTy);
4657+
4658+
return !derivedInterfaceTy->matches(overrideInterfaceTy,
4659+
TypeMatchFlags::AllowABICompatible,
4660+
/*resolver*/nullptr);
4661+
}
4662+
4663+
void AbstractFunctionDecl::computeNeedsNewVTableEntry() {
4664+
setNeedsNewVTableEntry(requiresNewVTableEntry(this));
4665+
}
4666+
4667+
46114668
FuncDecl *FuncDecl::createImpl(ASTContext &Context,
46124669
SourceLoc StaticLoc,
46134670
StaticSpellingKind StaticSpelling,
@@ -4762,7 +4819,7 @@ ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc,
47624819
setParameterLists(SelfDecl, BodyParams);
47634820

47644821
ConstructorDeclBits.ComputedBodyInitKind = 0;
4765-
ConstructorDeclBits.HasStubImplementation = 0;
4822+
this->HasStubImplementation = 0;
47664823
this->InitKind = static_cast<unsigned>(CtorInitializerKind::Designated);
47674824
this->Failability = static_cast<unsigned>(Failability);
47684825
}

0 commit comments

Comments
 (0)