Skip to content

[SILGen] Always serialize witness tables in non-resilient modules #20390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/ProtocolConformance.h
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,10 @@ class NormalProtocolConformance : public ProtocolConformance,
/// modules, but in a manner that ensures that all copies are equivalent.
bool isSynthesizedNonUnique() const;

/// Whether clients from outside the module can rely on the value witnesses
/// being consistent across versions of the framework.
bool isResilient() const;

/// Retrieve the type witness and type decl (if one exists)
/// for the given associated type.
std::pair<Type, TypeDecl *>
Expand Down
3 changes: 2 additions & 1 deletion include/swift/SIL/SILWitnessTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,8 @@ class SILWitnessTable : public llvm::ilist_node<SILWitnessTable>,
IsSerialized_t isSerialized);

// Whether a conformance should be serialized.
static bool conformanceIsSerialized(ProtocolConformance *conformance);
static bool
conformanceIsSerialized(const NormalProtocolConformance *conformance);

/// Call \c fn on each (split apart) conditional requirement of \c conformance
/// that should appear in a witness table, i.e., conformance requirements that
Expand Down
17 changes: 17 additions & 0 deletions lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,23 @@ bool NormalProtocolConformance::isSynthesizedNonUnique() const {
return isa<ClangModuleUnit>(getDeclContext()->getModuleScopeContext());
}

bool NormalProtocolConformance::isResilient() const {
// If the type is non-resilient or the module we're in is non-resilient, the
// conformance is non-resilient.
// FIXME: Looking at the type is not the right long-term solution. We need an
// explicit mechanism for declaring conformances as 'fragile', or even
// individual witnesses.
if (!getType()->getAnyNominal()->isResilient())
return false;

switch (getDeclContext()->getParentModule()->getResilienceStrategy()) {
case ResilienceStrategy::Resilient:
return true;
case ResilienceStrategy::Default:
return false;
}
}

Optional<ArrayRef<Requirement>>
ProtocolConformance::getConditionalRequirementsIfAvailable() const {
CONFORMANCE_SUBCLASS_DISPATCH(getConditionalRequirementsIfAvailable, ());
Expand Down
18 changes: 9 additions & 9 deletions lib/SIL/SILWitnessTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,21 +151,21 @@ void SILWitnessTable::convertToDefinition(
}
}

bool SILWitnessTable::conformanceIsSerialized(ProtocolConformance *conformance) {
bool SILWitnessTable::conformanceIsSerialized(
const NormalProtocolConformance *conformance) {
if (conformance->isResilient())
return false;

// Serialize witness tables for conformances synthesized by
// the ClangImporter.
if (isa<ClangModuleUnit>(conformance->getDeclContext()->getModuleScopeContext()))
return true;

if (conformance->getProtocol()->getEffectiveAccess() < AccessLevel::Public)
return false;

auto *nominal = conformance->getType()->getAnyNominal();
// Only serialize witness tables for fixed layout types.
//
// FIXME: This is not the right long term solution. We need an explicit
// mechanism for declaring conformances as 'fragile'.
auto protocolIsPublic =
conformance->getProtocol()->getEffectiveAccess() >= AccessLevel::Public;
auto typeIsPublic = nominal->getEffectiveAccess() >= AccessLevel::Public;
return !nominal->isResilient() && protocolIsPublic && typeIsPublic;
return nominal->getEffectiveAccess() >= AccessLevel::Public;
}

bool SILWitnessTable::enumerateWitnessTableConditionalConformances(
Expand Down
22 changes: 17 additions & 5 deletions test/SILGen/witness_tables_serialized.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// RUN: %target-swift-emit-silgen -enable-sil-ownership %s | %FileCheck %s
// This file is also used by witness_tables_serialized_import.swift.

// RUN: %target-swift-emit-silgen -enable-sil-ownership %s | %FileCheck -check-prefix CHECK -check-prefix CHECK-NONRESILIENT %s
// RUN: %target-swift-emit-silgen -enable-sil-ownership -enable-resilience %s | %FileCheck -check-prefix CHECK -check-prefix CHECK-RESILIENT %s

public protocol PublicProtocol {}

Expand All @@ -8,11 +11,20 @@ internal protocol InternalProtocol {}
@_fixed_layout
public struct PublicStruct : PublicProtocol, InternalProtocol {}

public struct PublicResilientStruct : PublicProtocol, InternalProtocol {}

@usableFromInline
internal struct InternalStruct : PublicProtocol, InternalProtocol {}

// CHECK-LABEL: sil_witness_table [serialized] PublicStruct: PublicProtocol
// CHECK-LABEL: sil_witness_table [serialized] PublicStruct: InternalProtocol
// CHECK: sil_witness_table [serialized] PublicStruct: PublicProtocol
// CHECK: sil_witness_table [serialized] PublicStruct: InternalProtocol

// CHECK-NONRESILIENT: sil_witness_table [serialized] PublicResilientStruct: PublicProtocol
// CHECK-NONRESILIENT: sil_witness_table [serialized] PublicResilientStruct: InternalProtocol
// CHECK-RESILIENT: sil_witness_table PublicResilientStruct: PublicProtocol
// CHECK-RESILIENT: sil_witness_table PublicResilientStruct: InternalProtocol

// CHECK-LABEL: sil_witness_table [serialized] InternalStruct: PublicProtocol
// CHECK-LABEL: sil_witness_table [serialized] InternalStruct: InternalProtocol
// CHECK-NONRESILIENT: sil_witness_table [serialized] InternalStruct: PublicProtocol
// CHECK-NONRESILIENT: sil_witness_table [serialized] InternalStruct: InternalProtocol
// CHECK-RESILIENT: sil_witness_table InternalStruct: PublicProtocol
// CHECK-RESILIENT: sil_witness_table InternalStruct: InternalProtocol
15 changes: 15 additions & 0 deletions test/SILGen/witness_tables_serialized_import.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -enable-sil-ownership -emit-module %S/witness_tables_serialized.swift -o %t -enable-resilience
// RUN: %target-swift-emit-silgen -enable-sil-ownership -I %t %s | %FileCheck %s

import witness_tables_serialized

public protocol AnotherPublicProtocol {}

@usableFromInline
internal protocol AnotherInternalProtocol {}

extension PublicResilientStruct : AnotherPublicProtocol, AnotherInternalProtocol {}

// CHECK: sil_witness_table [serialized] PublicResilientStruct: AnotherPublicProtocol
// CHECK: sil_witness_table [serialized] PublicResilientStruct: AnotherInternalProtocol