mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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:
@@ -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
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
58
test/Interpreter/enum_unavailable_cases.swift
Normal file
58
test/Interpreter/enum_unavailable_cases.swift
Normal 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)
|
||||
Reference in New Issue
Block a user