IRGen: Exclude unavailable enum cases from runtime layout calculations.

When `-unavailable-decl-optimization=complete` is specified, exclude
unavailable enum cases from the runtime layout of enums with payloads. Without
this, the type metadata for unavailable types may be referenced by enum cases
with unavailable payloads and cause linker failures.

Resolves rdar://107483852
This commit is contained in:
Allan Shortlidge
2023-04-02 22:17:18 -07:00
parent 570c364cc4
commit ce97377218
9 changed files with 89 additions and 11 deletions

View File

@@ -1086,11 +1086,11 @@ LLVM_LIBRARY_VISIBILITY bool usesObjCAllocator(ClassDecl *theClass);
/// Returns true if SIL/IR lowering for the given declaration should be skipped.
/// A declaration may not require lowering if, for example, it is annotated as
/// unavailable and optimization settings allow it to be omitted.
LLVM_LIBRARY_VISIBILITY bool shouldSkipLowering(Decl *D);
LLVM_LIBRARY_VISIBILITY bool shouldSkipLowering(const Decl *D);
/// Returns true if SIL/IR lowering for the given declaration should produce
/// a stub that traps at runtime because the code ought to be unreachable.
LLVM_LIBRARY_VISIBILITY bool shouldLowerToUnavailableCodeStub(Decl *D);
LLVM_LIBRARY_VISIBILITY bool shouldLowerToUnavailableCodeStub(const Decl *D);
} // namespace Lowering
/// Apply the given function to each ABI member of \c D skipping the members

View File

@@ -1568,7 +1568,9 @@ void IRGenerator::noteUseOfTypeGlobals(NominalTypeDecl *type,
RequireMetadata_t requireMetadata) {
if (!type)
return;
assert(!Lowering::shouldSkipLowering(type));
// Force emission of ObjC protocol descriptors used by type refs.
if (auto proto = dyn_cast<ProtocolDecl>(type)) {
if (proto->isObjC()) {

View File

@@ -6040,6 +6040,13 @@ EnumImplStrategy::get(TypeConverter &TC, SILType type, EnumDecl *theEnum) {
continue;
}
// For the purposes of memory layout, treat unavailable cases as if they do
// not have a payload.
if (Lowering::shouldSkipLowering(elt)) {
elementsWithNoPayload.push_back({elt, nullptr, nullptr});
continue;
}
// If the payload is indirect, we can use the NativeObject type metadata
// without recurring. The box won't affect loadability or fixed-ness.
if (elt->isIndirect() || theEnum->isIndirect()) {

View File

@@ -854,8 +854,11 @@ private:
if (hasPayload && (decl->isIndirect() || enumDecl->isIndirect()))
flags.setIsIndirectCase();
addField(flags, decl->getArgumentInterfaceType(),
decl->getBaseIdentifier().str());
Type interfaceType = Lowering::shouldSkipLowering(decl)
? nullptr
: decl->getArgumentInterfaceType();
addField(flags, interfaceType, decl->getBaseIdentifier().str());
}
void layoutEnum() {

View File

@@ -36,6 +36,7 @@
#include "swift/IRGen/ValueWitness.h"
#include "swift/SIL/RuntimeEffect.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILModule.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Hashing.h"
@@ -455,6 +456,7 @@ public:
}
void noteLazyReemissionOfNominalTypeDescriptor(NominalTypeDecl *decl) {
assert(!Lowering::shouldSkipLowering(decl));
LazilyReemittedTypeContextDescriptors.insert(decl);
}
@@ -464,6 +466,7 @@ public:
}
void noteUseOfMetadataAccessor(NominalTypeDecl *decl) {
assert(!Lowering::shouldSkipLowering(decl));
if (LazyMetadataAccessors.count(decl) == 0) {
LazyMetadataAccessors.insert(decl);
}

View File

@@ -34,6 +34,7 @@
#include "TupleMetadataVisitor.h"
#include "swift/Basic/LLVM.h"
#include "swift/SIL/SILModule.h"
#include "llvm/ADT/Optional.h"
using namespace swift;

View File

@@ -956,7 +956,7 @@ bool Lowering::usesObjCAllocator(ClassDecl *theClass) {
return theClass->getObjectModel() == ReferenceCounting::ObjC;
}
bool Lowering::shouldSkipLowering(Decl *D) {
bool Lowering::shouldSkipLowering(const Decl *D) {
if (D->getASTContext().LangOpts.UnavailableDeclOptimizationMode !=
UnavailableDeclOptimization::Complete)
return false;
@@ -966,7 +966,7 @@ bool Lowering::shouldSkipLowering(Decl *D) {
return D->getSemanticUnavailableAttr() != None;
}
bool Lowering::shouldLowerToUnavailableCodeStub(Decl *D) {
bool Lowering::shouldLowerToUnavailableCodeStub(const Decl *D) {
if (D->getASTContext().LangOpts.UnavailableDeclOptimizationMode !=
UnavailableDeclOptimization::Stub)
return false;

View File

@@ -10,6 +10,8 @@
// CHECK-NO-STRIP: private constant [25 x i8] c"unavailableEnumFirstCase\00"
// CHECK-STRIP-NOT: private constant [25 x i8] c"unavailableEnumFirstCase\00"
@available(*, unavailable)
public struct UnavailableStruct {}
public enum AvailableEnum {
case availableEnumAvailableCase
@@ -17,16 +19,18 @@ public enum AvailableEnum {
@available(*, unavailable)
case availableEnumUnavailableCase
@available(*, unavailable)
case availableEnumUnavailableCaseWithAssociatedValue(UnavailableStruct)
// CHECK-NO-STRIP: s4Test13AvailableEnumO17unavailableMethodyyF
// CHECK-STRIP-NOT: s4Test13AvailableEnumO17unavailableMethodyyF
@available(*, unavailable)
public func unavailableMethod() {}
// CHECK: s4Test13AvailableEnumO21__derived_enum_equalsySbAC_ACtFZ
// CHECK: s4Test13AvailableEnumO4hash4intoys6HasherVz_tF
// CHECK: s4Test13AvailableEnumO9hashValueSivg
}
// CHECK-NO-STRIP: s4Test17UnavailableStructVMa
// CHECK-STRIP-NOT: s4Test17UnavailableStructVMa
@available(*, unavailable)
public enum UnavailableEnum {
case unavailableEnumFirstCase

View File

@@ -0,0 +1,58 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -Xfrontend -unavailable-decl-optimization=complete -o %t/a.out
// RUN: %target-codesign %t/a.out
// RUN: %target-run %t/a.out
// REQUIRES: executable_test
import StdlibUnittest
@available(*, unavailable)
class UnavailableClass {
var x: UInt8 = 0
}
enum SingletonTrivial {
@available(*, unavailable)
case unavailable(UInt8)
}
enum SingletonClass {
@available(*, unavailable)
case unavailable(UnavailableClass)
}
enum NoPayload {
case x
@available(*, unavailable)
case unavailable
case y
}
enum SinglePayloadTrivial {
case x
@available(*, unavailable)
case unavailable(UInt8)
case y
}
enum MultiPayloadTrivial {
case x(UInt8)
@available(*, unavailable)
case unavailable(UInt8, UInt8)
case y
}
enum MultiPayloadGeneric<T, U> {
case x(T)
@available(*, unavailable)
case unavailable(T, U)
case y
}
expectEqual(MemoryLayout<SingletonTrivial>.size, 0)
expectEqual(MemoryLayout<SingletonClass>.size, 0)
expectEqual(MemoryLayout<NoPayload>.size, 1)
expectEqual(MemoryLayout<SinglePayloadTrivial>.size, 1)
expectEqual(MemoryLayout<MultiPayloadTrivial>.size, 2)
expectEqual(MemoryLayout<MultiPayloadGeneric<UInt8, UInt8>>.size, 2)
expectEqual(MemoryLayout<MultiPayloadGeneric<UInt32, UInt32>>.size, 5)