Skip to content

Commit 436b75b

Browse files
authored
Merge pull request #59951 from xedin/se-360-support-unavailable
[TypeChecker] SE-360: Add support for `if #unavailable`
2 parents 01d7d51 + 498721c commit 436b75b

File tree

11 files changed

+118
-48
lines changed

11 files changed

+118
-48
lines changed

include/swift/AST/Decl.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2952,9 +2952,12 @@ class OpaqueTypeDecl final :
29522952
return false;
29532953
}
29542954

2955+
using AvailabilityCondition = std::pair<VersionRange, bool>;
2956+
29552957
class ConditionallyAvailableSubstitutions final
2956-
: private llvm::TrailingObjects<ConditionallyAvailableSubstitutions,
2957-
VersionRange> {
2958+
: private llvm::TrailingObjects<
2959+
ConditionallyAvailableSubstitutions,
2960+
AvailabilityCondition> {
29582961
friend TrailingObjects;
29592962

29602963
unsigned NumAvailabilityConditions;
@@ -2964,25 +2967,25 @@ class OpaqueTypeDecl final :
29642967
/// A type with limited availability described by the provided set
29652968
/// of availability conditions (with `and` relationship).
29662969
ConditionallyAvailableSubstitutions(
2967-
ArrayRef<VersionRange> availabilityContext,
2970+
ArrayRef<AvailabilityCondition> availabilityContext,
29682971
SubstitutionMap substitutions)
29692972
: NumAvailabilityConditions(availabilityContext.size()),
29702973
Substitutions(substitutions) {
29712974
assert(!availabilityContext.empty());
29722975
std::uninitialized_copy(availabilityContext.begin(),
29732976
availabilityContext.end(),
2974-
getTrailingObjects<VersionRange>());
2977+
getTrailingObjects<AvailabilityCondition>());
29752978
}
29762979

29772980
public:
2978-
ArrayRef<VersionRange> getAvailability() const {
2979-
return {getTrailingObjects<VersionRange>(), NumAvailabilityConditions};
2981+
ArrayRef<AvailabilityCondition> getAvailability() const {
2982+
return {getTrailingObjects<AvailabilityCondition>(), NumAvailabilityConditions};
29802983
}
29812984

29822985
SubstitutionMap getSubstitutions() const { return Substitutions; }
29832986

29842987
static ConditionallyAvailableSubstitutions *
2985-
get(ASTContext &ctx, ArrayRef<VersionRange> availabilityContext,
2988+
get(ASTContext &ctx, ArrayRef<AvailabilityCondition> availabilityContext,
29862989
SubstitutionMap substitutions);
29872990
};
29882991
};

lib/AST/Decl.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8263,9 +8263,11 @@ void OpaqueTypeDecl::setConditionallyAvailableSubstitutions(
82638263

82648264
OpaqueTypeDecl::ConditionallyAvailableSubstitutions *
82658265
OpaqueTypeDecl::ConditionallyAvailableSubstitutions::get(
8266-
ASTContext &ctx, ArrayRef<VersionRange> availabilityContext,
8266+
ASTContext &ctx,
8267+
ArrayRef<AvailabilityCondition> availabilityContext,
82678268
SubstitutionMap substitutions) {
8268-
auto size = totalSizeToAlloc<VersionRange>(availabilityContext.size());
8269+
auto size =
8270+
totalSizeToAlloc<AvailabilityCondition>(availabilityContext.size());
82698271
auto mem = ctx.Allocate(size, alignof(ConditionallyAvailableSubstitutions));
82708272
return new (mem)
82718273
ConditionallyAvailableSubstitutions(availabilityContext, substitutions);

lib/IRGen/GenMeta.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2333,9 +2333,10 @@ namespace {
23332333
for (unsigned condIndex : indices(conditions)) {
23342334
const auto &condition = conditions[condIndex];
23352335

2336-
assert(condition.hasLowerEndpoint());
2336+
assert(condition.first.hasLowerEndpoint());
23372337

2338-
auto version = condition.getLowerEndpoint();
2338+
bool isUnavailability = condition.second;
2339+
auto version = condition.first.getLowerEndpoint();
23392340
auto *major = getInt32Constant(version.getMajor());
23402341
auto *minor = getInt32Constant(version.getMinor());
23412342
auto *patch = getInt32Constant(version.getSubminor());
@@ -2348,6 +2349,13 @@ namespace {
23482349
auto success = IGF.Builder.CreateICmpNE(
23492350
isAtLeast, llvm::Constant::getNullValue(IGM.Int32Ty));
23502351

2352+
if (isUnavailability) {
2353+
// Invert the result of "at least" check by xor'ing resulting
2354+
// boolean with `-1`.
2355+
success =
2356+
IGF.Builder.CreateXor(success, IGF.Builder.getIntN(1, -1));
2357+
}
2358+
23512359
auto nextCondOrRet = condIndex == conditions.size() - 1
23522360
? returnTypeBB
23532361
: conditionBlocks[condIndex + 1];
@@ -2367,7 +2375,7 @@ namespace {
23672375
auto universal = substitutionSet.back();
23682376

23692377
assert(universal->getAvailability().size() == 1 &&
2370-
universal->getAvailability()[0].isEmpty());
2378+
universal->getAvailability()[0].first.isEmpty());
23712379

23722380
IGF.Builder.CreateRet(
23732381
getResultValue(IGF, genericEnv, universal->getSubstitutions()));

lib/Sema/MiscDiagnostics.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2809,6 +2809,8 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
28092809
// There is no clear winner here since there are candidates within
28102810
// limited availability contexts.
28112811
void finalizeOpaque(const Candidate &universallyAvailable) {
2812+
using AvailabilityCondition = OpaqueTypeDecl::AvailabilityCondition;
2813+
28122814
SmallVector<OpaqueTypeDecl::ConditionallyAvailableSubstitutions *, 4>
28132815
conditionalSubstitutions;
28142816

@@ -2819,12 +2821,14 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
28192821
if (!availabilityContext)
28202822
continue;
28212823

2822-
SmallVector<VersionRange, 4> conditions;
2824+
SmallVector<AvailabilityCondition, 4> conditions;
28232825

28242826
llvm::transform(availabilityContext->getCond(),
28252827
std::back_inserter(conditions),
28262828
[&](const StmtConditionElement &elt) {
2827-
return elt.getAvailability()->getAvailableRange();
2829+
auto condition = elt.getAvailability();
2830+
return std::make_pair(condition->getAvailableRange(),
2831+
condition->isUnavailability());
28282832
});
28292833

28302834
conditionalSubstitutions.push_back(
@@ -2836,7 +2840,7 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
28362840
// Add universally available choice as the last one.
28372841
conditionalSubstitutions.push_back(
28382842
OpaqueTypeDecl::ConditionallyAvailableSubstitutions::get(
2839-
Ctx, {VersionRange::empty()},
2843+
Ctx, {{VersionRange::empty(), /*unavailable=*/false}},
28402844
std::get<1>(universallyAvailable)
28412845
.mapReplacementTypesOutOfContext()));
28422846

@@ -2882,12 +2886,10 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
28822886
return {true, S};
28832887
}
28842888

2885-
// If this is `if #available` statement with no other dynamic
2889+
// If this is `if #(un)available` statement with no other dynamic
28862890
// conditions, let's check if it returns opaque type directly.
28872891
if (llvm::all_of(If->getCond(), [&](const auto &condition) {
2888-
return condition.getKind() ==
2889-
StmtConditionElement::CK_Availability &&
2890-
!condition.getAvailability()->isUnavailability();
2892+
return condition.getKind() == StmtConditionElement::CK_Availability;
28912893
})) {
28922894
// Check return statement directly with availability context set.
28932895
if (auto *Then = dyn_cast<BraceStmt>(If->getThenStmt())) {

lib/Serialization/DeclTypeRecordNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ OTHER(DERIVATIVE_FUNCTION_CONFIGURATION, 154)
199199
OTHER(ERROR_FLAG, 155)
200200

201201
TRAILING_INFO(CONDITIONAL_SUBSTITUTION)
202+
TRAILING_INFO(CONDITIONAL_SUBSTITUTION_COND)
202203

203204
#ifndef DECL_ATTR
204205
#define DECL_ATTR(NAME, CLASS, OPTIONS, CODE) RECORD_VAL(CLASS##_DECL_ATTR, 180+CODE)

lib/Serialization/Deserialization.cpp

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3548,6 +3548,40 @@ class DeclDeserializer {
35483548
return deserializeAnyFunc(scratch, blobData, /*isAccessor*/true);
35493549
}
35503550

3551+
void deserializeConditionalSubstitutionConditions(
3552+
SmallVectorImpl<OpaqueTypeDecl::AvailabilityCondition> &conditions) {
3553+
using namespace decls_block;
3554+
3555+
SmallVector<uint64_t, 4> scratch;
3556+
StringRef blobData;
3557+
3558+
while (true) {
3559+
llvm::BitstreamEntry entry =
3560+
MF.fatalIfUnexpected(MF.DeclTypeCursor.advance(AF_DontPopBlockAtEnd));
3561+
if (entry.Kind != llvm::BitstreamEntry::Record)
3562+
break;
3563+
3564+
scratch.clear();
3565+
3566+
unsigned recordID = MF.fatalIfUnexpected(
3567+
MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData));
3568+
if (recordID != decls_block::CONDITIONAL_SUBSTITUTION_COND)
3569+
break;
3570+
3571+
bool isUnavailability;
3572+
DEF_VER_TUPLE_PIECES(condition);
3573+
3574+
ConditionalSubstitutionConditionLayout::readRecord(
3575+
scratch, isUnavailability, LIST_VER_TUPLE_PIECES(condition));
3576+
3577+
llvm::VersionTuple condition;
3578+
DECODE_VER_TUPLE(condition);
3579+
3580+
conditions.push_back(std::make_pair(VersionRange::allGTE(condition),
3581+
isUnavailability));
3582+
}
3583+
}
3584+
35513585
void deserializeConditionalSubstitutions(
35523586
SmallVectorImpl<OpaqueTypeDecl::ConditionallyAvailableSubstitutions *>
35533587
&limitedAvailability) {
@@ -3566,20 +3600,16 @@ class DeclDeserializer {
35663600
if (recordID != decls_block::CONDITIONAL_SUBSTITUTION)
35673601
break;
35683602

3569-
ArrayRef<uint64_t> rawConditions;
35703603
SubstitutionMapID substitutionMapRef;
35713604

35723605
decls_block::ConditionalSubstitutionLayout::readRecord(
3573-
scratch, substitutionMapRef, rawConditions);
3606+
scratch, substitutionMapRef);
3607+
3608+
SmallVector<OpaqueTypeDecl::AvailabilityCondition, 2> conditions;
3609+
deserializeConditionalSubstitutionConditions(conditions);
35743610

3575-
SmallVector<VersionRange, 4> conditions;
3576-
llvm::transform(rawConditions, std::back_inserter(conditions),
3577-
[&](uint64_t id) {
3578-
llvm::VersionTuple lowerEndpoint;
3579-
if (lowerEndpoint.tryParse(MF.getIdentifier(id).str()))
3580-
MF.fatal();
3581-
return VersionRange::allGTE(lowerEndpoint);
3582-
});
3611+
if (conditions.empty())
3612+
MF.fatal();
35833613

35843614
auto subMapOrError = MF.getSubstitutionMapChecked(substitutionMapRef);
35853615
if (!subMapOrError)
@@ -3655,7 +3685,8 @@ class DeclDeserializer {
36553685
} else {
36563686
limitedAvailability.push_back(
36573687
OpaqueTypeDecl::ConditionallyAvailableSubstitutions::get(
3658-
ctx, VersionRange::empty(), subMapOrError.get()));
3688+
ctx, {{VersionRange::empty(), /*unavailability=*/false}},
3689+
subMapOrError.get()));
36593690

36603691
opaqueDecl->setConditionallyAvailableSubstitutions(limitedAvailability);
36613692
}

lib/Serialization/ModuleFormat.h

Lines changed: 10 additions & 3 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 = 697; // change local type mangling
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 698; // opaque decl with unavailability conditions
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///
@@ -1462,10 +1462,17 @@ namespace decls_block {
14621462
// - inlinable body text, if any
14631463
>;
14641464

1465+
using ConditionalSubstitutionConditionLayout = BCRecordLayout<
1466+
CONDITIONAL_SUBSTITUTION_COND,
1467+
BCFixed<1>, // is unavailable?
1468+
BC_AVAIL_TUPLE // the OS version triple.
1469+
>;
1470+
14651471
using ConditionalSubstitutionLayout = BCRecordLayout<
14661472
CONDITIONAL_SUBSTITUTION,
1467-
SubstitutionMapIDField,
1468-
BCArray<IdentifierIDField> // N conditions where each is <major>.<minor>.<patch>
1473+
SubstitutionMapIDField
1474+
// Trailed by N conditions that include a version and
1475+
// unavailability indicator.
14691476
>;
14701477

14711478
using OpaqueTypeLayout = BCRecordLayout<

lib/Serialization/Serialization.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3945,17 +3945,20 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
39453945
S.DeclTypeAbbrCodes[ConditionalSubstitutionLayout::Code];
39463946
for (const auto *subs :
39473947
opaqueDecl->getConditionallyAvailableSubstitutions().drop_back()) {
3948-
SmallVector<IdentifierID, 4> conditions;
3948+
ConditionalSubstitutionLayout::emitRecord(
3949+
S.Out, S.ScratchRecord, abbrCode,
3950+
S.addSubstitutionMapRef(subs->getSubstitutions()));
39493951

3952+
unsigned condAbbrCode =
3953+
S.DeclTypeAbbrCodes[ConditionalSubstitutionConditionLayout::Code];
39503954
for (const auto &condition : subs->getAvailability()) {
3951-
auto lowerEndpoint = condition.getLowerEndpoint();
3952-
conditions.push_back(
3953-
S.addUniquedStringRef(lowerEndpoint.getAsString()));
3955+
ENCODE_VER_TUPLE(osVersion, llvm::Optional<llvm::VersionTuple>(
3956+
condition.first.getLowerEndpoint()));
3957+
ConditionalSubstitutionConditionLayout::emitRecord(
3958+
S.Out, S.ScratchRecord, condAbbrCode,
3959+
/*isUnavailable=*/condition.second,
3960+
LIST_VER_TUPLE_PIECES(osVersion));
39543961
}
3955-
3956-
ConditionalSubstitutionLayout::emitRecord(
3957-
S.Out, S.ScratchRecord, abbrCode,
3958-
S.addSubstitutionMapRef(subs->getSubstitutions()), conditions);
39593962
}
39603963
}
39613964
}
@@ -5134,6 +5137,7 @@ void Serializer::writeAllDeclsAndTypes() {
51345137
registerDeclTypeAbbr<XRefLayout>();
51355138

51365139
registerDeclTypeAbbr<ConditionalSubstitutionLayout>();
5140+
registerDeclTypeAbbr<ConditionalSubstitutionConditionLayout>();
51375141

51385142
#define DECL_ATTR(X, NAME, ...) \
51395143
registerDeclTypeAbbr<NAME##DeclAttrLayout>();

test/Serialization/Inputs/opaque_with_limited_availability.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ public func test() -> some P {
5252
return Empty()
5353
}
5454

55+
public func testUnavailable() -> some P {
56+
if #unavailable(macOS 100.0.1) {
57+
return Tuple<(Int, Int)>((0, 1))
58+
}
59+
60+
return Empty()
61+
}
62+
5563
public func test_return_from_conditional() -> some P {
5664
if #available(macOS 10.15, *) {
5765
return Named()

test/Serialization/opaque_with_availability_cross_module.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,7 @@ Test().sayHello()
4545
let conditionalR = LimitedAvailOpaque.test_return_from_conditional()
4646
conditionalR.hello()
4747
// CHECK: Hello from Named
48+
49+
let unavailableTest = LimitedAvailOpaque.testUnavailable()
50+
unavailableTest.hello()
51+
// CHECK: Hello from Tuple

0 commit comments

Comments
 (0)