Skip to content

Commit cda347e

Browse files
committed
[Serialization] Fix deserializing opaque types for computed properties and subscripts
A client shouldn't know about the underlying type of an opaque type unless it can see the body of the naming decl. Attempting to read it can lead to accessing a hidden dependency and a compiler crash. This was protected by a check specific to function decls but var decls and subscripts were not handled. To support them we have to move this logic to the writer side where we have access to the full AbstractStorageDecl and write in the swifmodule whether the underlying type should be visible outside of the module. rdar://117607906
1 parent 040e7a7 commit cda347e

File tree

6 files changed

+105
-11
lines changed

6 files changed

+105
-11
lines changed

include/swift/AST/Decl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3325,6 +3325,9 @@ class OpaqueTypeDecl final :
33253325
};
33263326
}
33273327

3328+
/// Should the underlying type be visible to clients outside of the module?
3329+
bool exportUnderlyingType() const;
3330+
33283331
/// The substitutions that map the generic parameters of the opaque type to
33293332
/// the unique underlying types, when that information is known.
33303333
llvm::Optional<SubstitutionMap> getUniqueUnderlyingTypeSubstitutions() const {

lib/AST/Decl.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9548,6 +9548,25 @@ GenericTypeParamDecl *OpaqueTypeDecl::getExplicitGenericParam(
95489548
return genericParamType->getDecl();
95499549
}
95509550

9551+
bool OpaqueTypeDecl::exportUnderlyingType() const {
9552+
auto mod = getDeclContext()->getParentModule();
9553+
if (mod->getResilienceStrategy() != ResilienceStrategy::Resilient)
9554+
return true;
9555+
9556+
ValueDecl *namingDecl = getNamingDecl();
9557+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(namingDecl))
9558+
return AFD->getResilienceExpansion() == ResilienceExpansion::Minimal;
9559+
9560+
if (auto *ASD = dyn_cast<AbstractStorageDecl>(namingDecl)) {
9561+
for (auto *accessor : ASD->getAllAccessors())
9562+
if (accessor->getResilienceExpansion() == ResilienceExpansion::Minimal)
9563+
return true;
9564+
return false;
9565+
}
9566+
9567+
llvm_unreachable("The naming decl is expected to be either an AFD or ASD");
9568+
}
9569+
95519570
llvm::Optional<unsigned>
95529571
OpaqueTypeDecl::getAnonymousOpaqueParamOrdinal(TypeRepr *repr) const {
95539572
assert(NamingDeclAndHasOpaqueReturnTypeRepr.getInt() &&

lib/Serialization/Deserialization.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4221,11 +4221,13 @@ class DeclDeserializer {
42214221
GenericSignatureID genericSigID;
42224222
SubstitutionMapID underlyingTypeSubsID;
42234223
uint8_t rawAccessLevel;
4224+
bool exportUnderlyingType;
42244225
decls_block::OpaqueTypeLayout::readRecord(scratch, contextID,
42254226
namingDeclID, interfaceSigID,
42264227
interfaceTypeID, genericSigID,
42274228
underlyingTypeSubsID,
4228-
rawAccessLevel);
4229+
rawAccessLevel,
4230+
exportUnderlyingType);
42294231

42304232
auto declContext = MF.getDeclContext(contextID);
42314233
auto interfaceSigOrErr = MF.getGenericSignatureChecked(interfaceSigID);
@@ -4261,16 +4263,25 @@ class DeclDeserializer {
42614263
else
42624264
opaqueDecl->setGenericSignature(GenericSignature());
42634265

4264-
auto *AFD = dyn_cast<AbstractFunctionDecl>(namingDecl);
4265-
if (MF.getResilienceStrategy() == ResilienceStrategy::Resilient &&
4266-
!MF.FileContext->getParentModule()->isMainModule() &&
4267-
AFD && AFD->getResilienceExpansion() != ResilienceExpansion::Minimal) {
4266+
if (!MF.FileContext->getParentModule()->isMainModule() &&
4267+
!exportUnderlyingType) {
42684268
// Do not try to read the underlying type information if the function
42694269
// is not inlinable in clients. This reflects the swiftinterface behavior
42704270
// in where clients are only aware of the underlying type when the body
42714271
// of the function is public.
4272+
LLVM_DEBUG(
4273+
llvm::dbgs() << "Ignoring underlying information for opaque type of '";
4274+
llvm::dbgs() << namingDecl->getName();
4275+
llvm::dbgs() << "'\n";
4276+
);
42724277

42734278
} else if (underlyingTypeSubsID) {
4279+
LLVM_DEBUG(
4280+
llvm::dbgs() << "Loading underlying information for opaque type of '";
4281+
llvm::dbgs() << namingDecl->getName();
4282+
llvm::dbgs() << "'\n";
4283+
);
4284+
42744285
auto subMapOrError = MF.getSubstitutionMapChecked(underlyingTypeSubsID);
42754286
if (!subMapOrError) {
42764287
// If the underlying type references internal details, ignore it.

lib/Serialization/ModuleFormat.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 816; // throw_addr
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 817; // Opaque type export details
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///
@@ -1641,7 +1641,8 @@ namespace decls_block {
16411641
TypeIDField, // interface type for opaque type
16421642
GenericSignatureIDField, // generic environment
16431643
SubstitutionMapIDField, // optional substitution map for underlying type
1644-
AccessLevelField // access level
1644+
AccessLevelField, // access level
1645+
BCFixed<1> // export underlying type details
16451646
// trailed by generic parameters
16461647
// trailed by conditional substitutions
16471648
>;

lib/Serialization/Serialization.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4490,11 +4490,14 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
44904490
}
44914491
uint8_t rawAccessLevel =
44924492
getRawStableAccessLevel(opaqueDecl->getFormalAccess());
4493+
bool exportDetails = opaqueDecl->exportUnderlyingType();
4494+
44934495
unsigned abbrCode = S.DeclTypeAbbrCodes[OpaqueTypeLayout::Code];
44944496
OpaqueTypeLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode,
44954497
contextID.getOpaqueValue(), namingDeclID,
44964498
interfaceSigID, interfaceTypeID, genericSigID,
4497-
underlyingSubsID, rawAccessLevel);
4499+
underlyingSubsID, rawAccessLevel,
4500+
exportDetails);
44984501
writeGenericParams(opaqueDecl->getGenericParams());
44994502

45004503
// Serialize all of the conditionally available substitutions expect the

test/Serialization/Safety/skip-reading-internal-details.swift

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
// RUN: -enable-library-evolution -swift-version 5 \
99
// RUN: -emit-module-path %t/HiddenLib.swiftmodule
1010
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -I %t \
11-
// RUN: -enable-library-evolution \
11+
// RUN: -swift-version 5 -enable-library-evolution \
1212
// RUN: -emit-module-path %t/Lib.swiftmodule \
13-
// RUN: -emit-module-interface-path %t/Lib.swiftinterface
13+
// RUN: -emit-module-interface-path %t/Lib.swiftinterface -verify
1414

1515
/// Build clients, with and without safety.
1616
// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \
@@ -107,12 +107,58 @@ public struct EV : V {
107107

108108
@available(SwiftStdlib 5.1, *)
109109
public extension V {
110+
private func referencedPrivateFunc(v: some V) -> some V { return v }
111+
112+
/// Hidden underlying types.
113+
// NEEDED: Ignoring underlying information for opaque type of 'opaqueReferencingPrivate()'
110114
@VB
111115
func opaqueReferencingPrivate() -> some V {
112116
referencedPrivateFunc(v: EV())
113117
}
114118

115-
private func referencedPrivateFunc(v: some V) -> some V { return v }
119+
// NEEDED: Ignoring underlying information for opaque type of 'opaqueReferencingPrivateVar'
120+
var opaqueReferencingPrivateVar: some V {
121+
referencedPrivateFunc(v: EV())
122+
}
123+
124+
// NEEDED: Ignoring underlying information for opaque type of 'opaqueReferencingPrivateVarPattern'
125+
var opaqueReferencingPrivateVarPattern: some V {
126+
get {
127+
referencedPrivateFunc(v: EV())
128+
}
129+
}
130+
131+
// NEEDED: Ignoring underlying information for opaque type of 'subscript(_:)'
132+
subscript(v: some V) -> some V {
133+
referencedPrivateFunc(v: v)
134+
}
135+
136+
/// Visible underlying types.
137+
// NEEDED: Loading underlying information for opaque type of 'inlinableOpaqueFunc()'
138+
@inlinable
139+
func inlinableOpaqueFunc() -> some V { EV() }
140+
141+
// NEEDED: Loading underlying information for opaque type of 'backdeployedOpaqueFunc()'
142+
@backDeployed(before: SwiftStdlib 5.1) // expected-warning 4 {{'@backDeployed' is unsupported on a instance method with a 'some' return type}}
143+
func backdeployedOpaqueFunc() -> some V { EV() }
144+
145+
// NEEDED: Loading underlying information for opaque type of 'aeicOpaqueFunc()'
146+
@_alwaysEmitIntoClient
147+
func aeicOpaqueFunc() -> some V { EV() }
148+
149+
// NEEDED: Loading underlying information for opaque type of 'transparentOpaqueFunc()'
150+
@_transparent
151+
func transparentOpaqueFunc() -> some V { EV() }
152+
153+
// NEEDED: Loading underlying information for opaque type of 'inlinableOpaqueVar'
154+
@inlinable
155+
var inlinableOpaqueVar: some V { EV() }
156+
157+
// NEEDED: Loading underlying information for opaque type of 'inlinableOpaqueVarPattern'
158+
var inlinableOpaqueVarPattern: some V {
159+
@inlinable
160+
get { EV() }
161+
}
116162
}
117163

118164
//--- Client.swift
@@ -127,4 +173,15 @@ x.notAMember() // expected-error {{value of type 'PublicStruct' has no member 'n
127173
if #available(SwiftStdlib 5.1, *) {
128174
let v = EV()
129175
let _ = v.opaqueReferencingPrivate()
176+
let _ = v.opaqueReferencingPrivateVar
177+
let _ = v.opaqueReferencingPrivateVarPattern
178+
let _ = v[v]
179+
180+
let _ = v.inlinableOpaqueFunc()
181+
let _ = v.backdeployedOpaqueFunc()
182+
let _ = v.aeicOpaqueFunc()
183+
let _ = v.transparentOpaqueFunc()
184+
185+
let _ = v.inlinableOpaqueVar
186+
let _ = v.inlinableOpaqueVarPattern
130187
}

0 commit comments

Comments
 (0)