Skip to content

Commit 66b48b0

Browse files
committed
[Serialization] Opaque types: serialize conditionally available underlying types
Resolves: rdar://90595158 (cherry picked from commit cd17bee)
1 parent 013270a commit 66b48b0

File tree

7 files changed

+217
-6
lines changed

7 files changed

+217
-6
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2903,6 +2903,10 @@ class OpaqueTypeDecl final :
29032903
UniqueUnderlyingType = subs;
29042904
}
29052905

2906+
bool hasConditionallyAvailableSubstitutions() const {
2907+
return ConditionallyAvailableTypes.hasValue();
2908+
}
2909+
29062910
ArrayRef<ConditionallyAvailableSubstitutions *>
29072911
getConditionallyAvailableSubstitutions() const {
29082912
assert(ConditionallyAvailableTypes);

lib/Serialization/DeclTypeRecordNodes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ OTHER(DERIVATIVE_FUNCTION_CONFIGURATION, 154)
198198

199199
OTHER(ERROR_FLAG, 155)
200200

201+
TRAILING_INFO(CONDITIONAL_SUBSTITUTION)
202+
201203
#ifndef DECL_ATTR
202204
#define DECL_ATTR(NAME, CLASS, OPTIONS, CODE) RECORD_VAL(CLASS##_DECL_ATTR, 180+CODE)
203205
#endif

lib/Serialization/Deserialization.cpp

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3517,7 +3517,50 @@ class DeclDeserializer {
35173517
StringRef blobData) {
35183518
return deserializeAnyFunc(scratch, blobData, /*isAccessor*/true);
35193519
}
3520-
3520+
3521+
void deserializeConditionalSubstitutions(
3522+
SmallVectorImpl<OpaqueTypeDecl::ConditionallyAvailableSubstitutions *>
3523+
&limitedAvailability) {
3524+
SmallVector<uint64_t, 4> scratch;
3525+
StringRef blobData;
3526+
3527+
while (true) {
3528+
llvm::BitstreamEntry entry =
3529+
MF.fatalIfUnexpected(MF.DeclTypeCursor.advance(AF_DontPopBlockAtEnd));
3530+
if (entry.Kind != llvm::BitstreamEntry::Record)
3531+
break;
3532+
3533+
scratch.clear();
3534+
unsigned recordID = MF.fatalIfUnexpected(
3535+
MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData));
3536+
if (recordID != decls_block::CONDITIONAL_SUBSTITUTION)
3537+
break;
3538+
3539+
ArrayRef<uint64_t> rawConditions;
3540+
SubstitutionMapID substitutionMapRef;
3541+
3542+
decls_block::ConditionalSubstitutionLayout::readRecord(
3543+
scratch, substitutionMapRef, rawConditions);
3544+
3545+
SmallVector<VersionRange, 4> conditions;
3546+
llvm::transform(rawConditions, std::back_inserter(conditions),
3547+
[&](uint64_t id) {
3548+
llvm::VersionTuple lowerEndpoint;
3549+
if (lowerEndpoint.tryParse(MF.getIdentifier(id).str()))
3550+
MF.fatal();
3551+
return VersionRange::allGTE(lowerEndpoint);
3552+
});
3553+
3554+
auto subMapOrError = MF.getSubstitutionMapChecked(substitutionMapRef);
3555+
if (!subMapOrError)
3556+
MF.fatal();
3557+
3558+
limitedAvailability.push_back(
3559+
OpaqueTypeDecl::ConditionallyAvailableSubstitutions::get(
3560+
ctx, conditions, subMapOrError.get()));
3561+
}
3562+
}
3563+
35213564
Expected<Decl *> deserializeOpaqueType(ArrayRef<uint64_t> scratch,
35223565
StringRef blobData) {
35233566
DeclID namingDeclID;
@@ -3568,7 +3611,24 @@ class DeclDeserializer {
35683611
auto subMapOrError = MF.getSubstitutionMapChecked(underlyingTypeSubsID);
35693612
if (!subMapOrError)
35703613
return subMapOrError.takeError();
3571-
opaqueDecl->setUniqueUnderlyingTypeSubstitutions(subMapOrError.get());
3614+
3615+
// Check whether there are any conditionally available substitutions.
3616+
// If there are, it means that "unique" we just read is a universally
3617+
// available substitution.
3618+
SmallVector<OpaqueTypeDecl::ConditionallyAvailableSubstitutions *>
3619+
limitedAvailability;
3620+
3621+
deserializeConditionalSubstitutions(limitedAvailability);
3622+
3623+
if (limitedAvailability.empty()) {
3624+
opaqueDecl->setUniqueUnderlyingTypeSubstitutions(subMapOrError.get());
3625+
} else {
3626+
limitedAvailability.push_back(
3627+
OpaqueTypeDecl::ConditionallyAvailableSubstitutions::get(
3628+
ctx, VersionRange::empty(), subMapOrError.get()));
3629+
3630+
opaqueDecl->setConditionallyAvailableSubstitutions(limitedAvailability);
3631+
}
35723632
}
35733633
return opaqueDecl;
35743634
}

lib/Serialization/ModuleFormat.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5656
/// describe what change you made. The content of this comment isn't important;
5757
/// it just ensures a conflict if two people change the module format.
5858
/// Don't worry about adhering to the 80-column limit for this line.
59-
const uint16_t SWIFTMODULE_VERSION_MINOR = 683; // Remove associatedtype primary bit
59+
const uint16_t SWIFTMODULE_VERSION_MINOR = 684; // Cond. available underlying types for opaque
6060

6161
/// A standard hash seed used for all string hashes in a serialized module.
6262
///
@@ -1413,7 +1413,13 @@ namespace decls_block {
14131413
// - the foreign error convention, if any
14141414
// - inlinable body text, if any
14151415
>;
1416-
1416+
1417+
using ConditionalSubstitutionLayout = BCRecordLayout<
1418+
CONDITIONAL_SUBSTITUTION,
1419+
SubstitutionMapIDField,
1420+
BCArray<IdentifierIDField> // N conditions where each is <major>.<minor>.<patch>
1421+
>;
1422+
14171423
using OpaqueTypeLayout = BCRecordLayout<
14181424
OPAQUE_TYPE_DECL,
14191425
DeclContextIDField, // decl context
@@ -1424,6 +1430,7 @@ namespace decls_block {
14241430
SubstitutionMapIDField, // optional substitution map for underlying type
14251431
AccessLevelField // access level
14261432
// trailed by generic parameters
1433+
// trailed by conditional substitutions
14271434
>;
14281435

14291436
// TODO: remove the unnecessary FuncDecl components here

lib/Serialization/Serialization.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3897,16 +3897,44 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
38973897
auto genericSigID = S.addGenericSignatureRef(opaqueDecl->getGenericSignature());
38983898

38993899
SubstitutionMapID underlyingSubsID = 0;
3900-
if (auto underlying = opaqueDecl->getUniqueUnderlyingTypeSubstitutions())
3900+
if (auto underlying = opaqueDecl->getUniqueUnderlyingTypeSubstitutions()) {
39013901
underlyingSubsID = S.addSubstitutionMapRef(*underlying);
3902+
} else if (opaqueDecl->hasConditionallyAvailableSubstitutions()) {
3903+
// Universally available type doesn't have any availability conditions
3904+
// so it could be serialized into "unique" slot to safe space.
3905+
auto universal =
3906+
opaqueDecl->getConditionallyAvailableSubstitutions().back();
3907+
underlyingSubsID = S.addSubstitutionMapRef(universal->getSubstitutions());
3908+
}
39023909
uint8_t rawAccessLevel =
3903-
getRawStableAccessLevel(opaqueDecl->getFormalAccess());
3910+
getRawStableAccessLevel(opaqueDecl->getFormalAccess());
39043911
unsigned abbrCode = S.DeclTypeAbbrCodes[OpaqueTypeLayout::Code];
39053912
OpaqueTypeLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode,
39063913
contextID.getOpaqueValue(), namingDeclID,
39073914
interfaceSigID, interfaceTypeID, genericSigID,
39083915
underlyingSubsID, rawAccessLevel);
39093916
writeGenericParams(opaqueDecl->getGenericParams());
3917+
3918+
// Serialize all of the conditionally available substitutions expect the
3919+
// last one - universal, it's serialized into "unique" slot.
3920+
if (opaqueDecl->hasConditionallyAvailableSubstitutions()) {
3921+
unsigned abbrCode =
3922+
S.DeclTypeAbbrCodes[ConditionalSubstitutionLayout::Code];
3923+
for (const auto *subs :
3924+
opaqueDecl->getConditionallyAvailableSubstitutions().drop_back()) {
3925+
SmallVector<IdentifierID, 4> conditions;
3926+
3927+
for (const auto &condition : subs->getAvailability()) {
3928+
auto lowerEndpoint = condition.getLowerEndpoint();
3929+
conditions.push_back(
3930+
S.addUniquedStringRef(lowerEndpoint.getAsString()));
3931+
}
3932+
3933+
ConditionalSubstitutionLayout::emitRecord(
3934+
S.Out, S.ScratchRecord, abbrCode,
3935+
S.addSubstitutionMapRef(subs->getSubstitutions()), conditions);
3936+
}
3937+
}
39103938
}
39113939

39123940
void visitAccessorDecl(const AccessorDecl *fn) {
@@ -5074,6 +5102,8 @@ void Serializer::writeAllDeclsAndTypes() {
50745102
registerDeclTypeAbbr<MembersLayout>();
50755103
registerDeclTypeAbbr<XRefLayout>();
50765104

5105+
registerDeclTypeAbbr<ConditionalSubstitutionLayout>();
5106+
50775107
#define DECL_ATTR(X, NAME, ...) \
50785108
registerDeclTypeAbbr<NAME##DeclAttrLayout>();
50795109
#include "swift/AST/Attr.def"
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
public protocol P {
2+
func hello()
3+
}
4+
5+
public struct Empty : P {
6+
public func hello() { print("Hello from Empty") }
7+
}
8+
9+
public struct Tuple<T>: P {
10+
public init(_ tuple: T) {}
11+
12+
public func hello() { print("Hello from Tuple") }
13+
}
14+
15+
@available(macOS 10.15, *)
16+
struct Named : P {
17+
public func hello() { print("Hello from Named") }
18+
}
19+
20+
@resultBuilder
21+
public struct Example {
22+
public static func buildOptional<T: P>(_ v: T?) -> some P {
23+
if #available(macOS 100.0.1, *) {
24+
let result = v!
25+
result.hello()
26+
return result
27+
} else {
28+
let result = Empty()
29+
result.hello()
30+
return result
31+
}
32+
}
33+
34+
public static func buildLimitedAvailability<T: P>(_ component: T) -> some P {
35+
component
36+
}
37+
38+
public static func buildBlock<T: P>(_ components: T) -> T {
39+
return components
40+
}
41+
42+
public static func buildBlock<T1: P, T2: P>(_ v1: T1, _ v2: T2) -> Tuple<(T1, T2)> {
43+
return Tuple((v1, v2))
44+
}
45+
}
46+
47+
public func test() -> some P {
48+
if #available(macOS 100.0.1, *) {
49+
return Tuple<(Int, Int)>((0, 0))
50+
}
51+
52+
return Empty()
53+
}
54+
55+
public func test_return_from_conditional() -> some P {
56+
if #available(macOS 10.15, *) {
57+
return Named()
58+
}
59+
60+
return Tuple<(String, Int)>(("", 0))
61+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -target %target-cpu-apple-macosx10.15 -parse-as-library -emit-library -emit-module-path %t/LimitedAvailOpaque.swiftmodule -module-name LimitedAvailOpaque %S/Inputs/opaque_with_limited_availability.swift -o %t/%target-library-name(LimitedAvailOpaque)
3+
// RUN: %target-build-swift -target %target-cpu-apple-macosx10.15 -lLimitedAvailOpaque -module-name main -I %t -L %t %s -o %t/a.out
4+
// RUN: %target-run %t/a.out | %FileCheck %s --color
5+
6+
// REQUIRES: OS=macosx && (CPU=x86_64 || CPU=arm64)
7+
// REQUIRES: executable_test
8+
9+
import LimitedAvailOpaque
10+
11+
struct S: P {
12+
func hello() { print("Hello from S") }
13+
}
14+
15+
@available(macOS 100.0.1, *)
16+
struct NewS: P {
17+
func hello() { print("Hello from NewS") }
18+
}
19+
20+
public struct Test {
21+
@Example
22+
var body: some P {
23+
// TODO(diagnostics): This is incorrect warning due to `some P` return of `buildWithLimitedAvailability`
24+
// expected-warning@+1 {{result builder 'Example' does not implement 'buildLimitedAvailability'; this code may crash on earlier versions of the OS}}
25+
if #available(macOS 100.0.1, *) {
26+
NewS()
27+
}
28+
29+
S()
30+
}
31+
32+
func sayHello() {
33+
body.hello()
34+
}
35+
}
36+
37+
let result = LimitedAvailOpaque.test()
38+
result.hello()
39+
// CHECK: Hello from Empty
40+
41+
Test().sayHello()
42+
// CHECK: Hello from Empty
43+
// CHECK: Hello from Tuple
44+
45+
let conditionalR = LimitedAvailOpaque.test_return_from_conditional()
46+
conditionalR.hello()
47+
// CHECK: Hello from Named

0 commit comments

Comments
 (0)