Skip to content

Commit 74dc00f

Browse files
authored
Merge pull request swiftlang#40677 from beccadax/sendable-to-the-whole-block
[ClangImporter] Make completion handlers Sendable
2 parents c8ab423 + 8c84412 commit 74dc00f

17 files changed

+264
-104
lines changed

lib/AST/ASTPrinter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5428,7 +5428,7 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
54285428

54295429
if (!Options.excludeAttrKind(TAK_Sendable) &&
54305430
info.isSendable()) {
5431-
Printer << "@Sendable ";
5431+
Printer.printSimpleAttr("@Sendable") << " ";
54325432
}
54335433

54345434
SmallString<64> buf;

lib/ClangImporter/ImportDecl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2796,6 +2796,15 @@ namespace {
27962796
SourceLoc(), Name,
27972797
Loc,
27982798
/*genericparams*/nullptr, DC);
2799+
2800+
// If the typedef is marked with @Sendable and not @_nonSendable, make
2801+
// any function type in it Sendable.
2802+
auto sendability = Result->getAttrs().getEffectiveSendableAttr();
2803+
if (isa_and_nonnull<SendableAttr>(sendability))
2804+
SwiftType = applyToFunctionType(SwiftType, [](ASTExtInfo info) {
2805+
return info.withConcurrent();
2806+
});
2807+
27992808
Result->setUnderlyingType(SwiftType);
28002809

28012810
// Make Objective-C's 'id' unavailable.

lib/ClangImporter/ImportName.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2270,6 +2270,17 @@ ImportedName NameImporter::importName(const clang::NamedDecl *decl,
22702270
}
22712271
++ImportNameNumCacheMisses;
22722272
auto res = importNameImpl(decl, version, givenName);
2273+
2274+
// Add information about the async version of the name to the non-async
2275+
// version of the name.
2276+
if (!version.supportsConcurrency()) {
2277+
if (auto importedAsyncName = importName(decl, version.withConcurrency(true),
2278+
givenName)) {
2279+
res.info.hasAsyncAlternateInfo = importedAsyncName.info.hasAsyncInfo;
2280+
res.info.asyncInfo = importedAsyncName.info.asyncInfo;
2281+
}
2282+
}
2283+
22732284
if (!givenName)
22742285
importNameCache[key] = res;
22752286
return res;

lib/ClangImporter/ImportName.h

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,11 +226,14 @@ class ImportedName {
226226

227227
unsigned hasAsyncInfo : 1;
228228

229+
unsigned hasAsyncAlternateInfo: 1;
230+
229231
Info()
230232
: errorInfo(), selfIndex(), initKind(CtorInitializerKind::Designated),
231233
accessorKind(ImportedAccessorKind::None), hasCustomName(false),
232234
droppedVariadic(false), importAsMember(false), hasSelfIndex(false),
233-
hasErrorInfo(false), hasAsyncInfo(false) {}
235+
hasErrorInfo(false), hasAsyncInfo(false),
236+
hasAsyncAlternateInfo(false) {}
234237
} info;
235238

236239
public:
@@ -267,8 +270,27 @@ class ImportedName {
267270
/// For names that map Objective-C methods with completion handlers into
268271
/// async Swift methods, describes how the mapping is performed.
269272
Optional<ForeignAsyncConvention::Info> getAsyncInfo() const {
270-
if (info.hasAsyncInfo)
273+
if (info.hasAsyncInfo) {
274+
assert(!info.hasAsyncAlternateInfo
275+
&& "both regular and alternate async info?");
276+
return info.asyncInfo;
277+
}
278+
return None;
279+
}
280+
281+
/// For names with a variant that maps Objective-C methods with completion
282+
/// handlers into async Swift methods, describes how the mapping is performed.
283+
///
284+
/// That is, if the method imports as both an async method and a completion
285+
/// handler method, this value is set on the completion handler method's name
286+
/// and gives you the contents of \c getAsyncInfo() on the async method's
287+
/// name. It is not set on the async method's name, and it is not set if a
288+
/// non-async method doesn't have an async equivalent.
289+
Optional<ForeignAsyncConvention::Info> getAsyncAlternateInfo() const {
290+
if (info.hasAsyncAlternateInfo) {
291+
assert(!info.hasAsyncInfo && "both regular and alternate async info?");
271292
return info.asyncInfo;
293+
}
272294
return None;
273295
}
274296

lib/ClangImporter/ImportType.cpp

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1754,63 +1754,58 @@ ImportedType ClangImporter::Implementation::importPropertyType(
17541754
Bridgeability::Full, optionality);
17551755
}
17561756

1757-
/// Apply an attribute to a function type.
1758-
static Type applyToFunctionType(
1759-
Type type, llvm::function_ref<ASTExtInfo(ASTExtInfo)> transform) {
1760-
// Recurse into optional types.
1761-
if (Type objectType = type->getOptionalObjectType()) {
1762-
return OptionalType::get(applyToFunctionType(objectType, transform));
1763-
}
1764-
1765-
// Apply @noescape to function types.
1766-
if (auto funcType = type->getAs<FunctionType>()) {
1767-
return FunctionType::get(funcType->getParams(), funcType->getResult(),
1768-
transform(funcType->getExtInfo()));
1769-
}
1770-
1771-
return type;
1772-
}
1773-
17741757
Type ClangImporter::Implementation::applyParamAttributes(
1775-
const clang::ParmVarDecl *param, Type type) {
1776-
if (!param->hasAttrs())
1777-
return type;
1758+
const clang::ParmVarDecl *param, Type type, bool sendableByDefault) {
1759+
bool sendableRequested = sendableByDefault;
1760+
bool sendableDisqualified = false;
1761+
1762+
if (param->hasAttrs()) {
1763+
for (auto attr : param->getAttrs()) {
1764+
// Map __attribute__((noescape)) to @noescape.
1765+
if (isa<clang::NoEscapeAttr>(attr)) {
1766+
type = applyToFunctionType(type, [](ASTExtInfo extInfo) {
1767+
return extInfo.withNoEscape();
1768+
});
17781769

1779-
for (auto attr : param->getAttrs()) {
1780-
// Map __attribute__((noescape)) to @noescape.
1781-
if (isa<clang::NoEscapeAttr>(attr)) {
1782-
type = applyToFunctionType(type, [](ASTExtInfo extInfo) {
1783-
return extInfo.withNoEscape();
1784-
});
1770+
continue;
1771+
}
17851772

1786-
continue;
1787-
}
1773+
auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr);
1774+
if (!swiftAttr)
1775+
continue;
17881776

1789-
auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr);
1790-
if (!swiftAttr)
1791-
continue;
1777+
// Map the main-actor attribute.
1778+
if (isMainActorAttr(swiftAttr)) {
1779+
if (Type mainActor = SwiftContext.getMainActorType()) {
1780+
type = applyToFunctionType(type, [&](ASTExtInfo extInfo) {
1781+
return extInfo.withGlobalActor(mainActor);
1782+
});
1783+
sendableDisqualified = true;
1784+
}
17921785

1793-
// Map the main-actor attribute.
1794-
if (isMainActorAttr(swiftAttr)) {
1795-
if (Type mainActor = SwiftContext.getMainActorType()) {
1796-
type = applyToFunctionType(type, [&](ASTExtInfo extInfo) {
1797-
return extInfo.withGlobalActor(mainActor);
1798-
});
1786+
continue;
17991787
}
18001788

1801-
continue;
1802-
}
1803-
1804-
// Map @Sendable.
1805-
if (swiftAttr->getAttribute() == "@Sendable") {
1806-
type = applyToFunctionType(type, [](ASTExtInfo extInfo) {
1807-
return extInfo.withConcurrent();
1808-
});
1789+
// Map @Sendable.
1790+
if (swiftAttr->getAttribute() == "@Sendable") {
1791+
sendableRequested = true;
1792+
continue;
1793+
}
18091794

1810-
continue;
1795+
// Map @_nonSendable.
1796+
if (swiftAttr->getAttribute() == "@_nonSendable") {
1797+
sendableDisqualified = true;
1798+
continue;
1799+
}
18111800
}
18121801
}
18131802

1803+
if (!sendableDisqualified && sendableRequested) {
1804+
type = applyToFunctionType(type, [](ASTExtInfo extInfo) {
1805+
return extInfo.withConcurrent();
1806+
});
1807+
}
1808+
18141809
return type;
18151810
}
18161811

@@ -2015,7 +2010,7 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
20152010

20162011
// Apply attributes to the type.
20172012
swiftParamTy = applyParamAttributes(
2018-
param, swiftParamTy);
2013+
param, swiftParamTy, /*sendableByDefault=*/false);
20192014

20202015
// Figure out the name for this parameter.
20212016
Identifier bodyName = importFullName(param, CurrentVersion)
@@ -2401,6 +2396,10 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
24012396
Optional<ForeignErrorConvention::Info> errorInfo =
24022397
importedName.getErrorInfo();
24032398
auto asyncInfo = importedName.getAsyncInfo();
2399+
bool isAsync = asyncInfo.hasValue();
2400+
if (!isAsync)
2401+
asyncInfo = importedName.getAsyncAlternateInfo();
2402+
24042403
OptionalTypeKind OptionalityOfReturn;
24052404
if (clangDecl->hasAttr<clang::ReturnsNonNullAttr>()) {
24062405
OptionalityOfReturn = OTK_None;
@@ -2547,7 +2546,7 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
25472546
// Figure out if this is a completion handler parameter whose error
25482547
// parameter is used to indicate throwing.
25492548
Optional<unsigned> completionHandlerErrorParamIndex;
2550-
if (paramIsCompletionHandler) {
2549+
if (isAsync && paramIsCompletionHandler) {
25512550
completionHandlerErrorParamIndex =
25522551
asyncInfo->completionHandlerErrorParamIndex();
25532552
}
@@ -2592,7 +2591,7 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
25922591

25932592
// If this is a completion handler, figure out it's effect on the result
25942593
// type but don't build it into the parameter type.
2595-
if (paramIsCompletionHandler) {
2594+
if (isAsync && paramIsCompletionHandler) {
25962595
if (Type replacedSwiftResultTy =
25972596
decomposeCompletionHandlerType(swiftParamTy, *asyncInfo)) {
25982597
swiftResultTy = replacedSwiftResultTy;
@@ -2611,7 +2610,8 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
26112610
}
26122611

26132612
// Apply Clang attributes to the parameter type.
2614-
swiftParamTy = applyParamAttributes(param, swiftParamTy);
2613+
swiftParamTy = applyParamAttributes(param, swiftParamTy,
2614+
/*sendableByDefault=*/paramIsCompletionHandler);
26152615

26162616
// Figure out the name for this parameter.
26172617
Identifier bodyName = importFullName(param, CurrentVersion)
@@ -2686,7 +2686,7 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
26862686
swiftResultTy = SwiftContext.getNeverType();
26872687
}
26882688

2689-
if (asyncInfo) {
2689+
if (isAsync) {
26902690
asyncConvention = ForeignAsyncConvention(
26912691
completionHandlerType, asyncInfo->completionHandlerParamIndex(),
26922692
asyncInfo->completionHandlerErrorParamIndex(),

lib/ClangImporter/ImporterImpl.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
937937
void importAttributes(const clang::NamedDecl *ClangDecl, Decl *MappedDecl,
938938
const clang::ObjCContainerDecl *NewContext = nullptr);
939939

940-
Type applyParamAttributes(const clang::ParmVarDecl *param, Type type);
940+
Type applyParamAttributes(const clang::ParmVarDecl *param, Type type,
941+
bool sendableByDefault);
941942

942943
/// If we already imported a given decl, return the corresponding Swift decl.
943944
/// Otherwise, return nullptr.
@@ -1752,6 +1753,25 @@ class SwiftNameLookupExtension : public clang::ModuleFileExtension {
17521753
/// actor.
17531754
bool isMainActorAttr(const clang::SwiftAttrAttr *swiftAttr);
17541755

1756+
/// Apply an attribute to a function type.
1757+
static inline Type applyToFunctionType(
1758+
Type type, llvm::function_ref<ASTExtInfo(ASTExtInfo)> transform) {
1759+
// Recurse into optional types.
1760+
if (Type objectType = type->getOptionalObjectType()) {
1761+
return OptionalType::get(applyToFunctionType(objectType, transform));
1762+
}
1763+
1764+
// Apply transform to function types.
1765+
if (auto funcType = type->getAs<FunctionType>()) {
1766+
auto newExtInfo = transform(funcType->getExtInfo());
1767+
if (!newExtInfo.isEqualTo(funcType->getExtInfo(), /*useClangTypes=*/true))
1768+
return FunctionType::get(funcType->getParams(), funcType->getResult(),
1769+
newExtInfo);
1770+
}
1771+
1772+
return type;
1773+
}
1774+
17551775
}
17561776
}
17571777

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,31 @@ getTypesToCompare(ValueDecl *reqt, Type reqtType, bool reqtTypeIsIUO,
140140
Type witnessType, bool witnessTypeIsIUO,
141141
VarianceKind variance) {
142142
// If the witness type is noescape but the requirement type is not,
143-
// adjust the witness type to be escaping. This permits a limited form of
144-
// covariance.
145-
bool reqNoescapeToEscaping = false;
146-
(void)adjustInferredAssociatedType(reqtType, reqNoescapeToEscaping);
147-
bool witnessNoescapeToEscaping = false;
148-
Type adjustedWitnessType =
149-
adjustInferredAssociatedType(witnessType, witnessNoescapeToEscaping);
150-
if (witnessNoescapeToEscaping && !reqNoescapeToEscaping)
151-
witnessType = adjustedWitnessType;
143+
// adjust the witness type to be escaping; likewisse for sendability. This
144+
// permits a limited form of covariance.
145+
auto applyAdjustment = [&](TypeAdjustment adjustment) {
146+
// Sometimes the witness has a function type, but the requirement has
147+
// something else (a dependent type we need to infer, in the most relevant
148+
// case). In that situation, should we behave as though the requirement type
149+
// *did* need the adjustment, or as though it *did not*?
150+
//
151+
// For noescape, we want to behave as though it was necessary because any
152+
// function type not in a parameter is, more or less, implicitly @escaping.
153+
// For Sendable, we want to behave as though it was not necessary because
154+
// function types that aren't in a parameter can be Sendable or not.
155+
// FIXME: Should we check for a Sendable bound on the requirement type?
156+
bool inRequirement = (adjustment != TypeAdjustment::NoescapeToEscaping);
157+
(void)adjustInferredAssociatedType(adjustment, reqtType, inRequirement);
158+
159+
bool inWitness = false;
160+
Type adjustedWitnessType =
161+
adjustInferredAssociatedType(adjustment, witnessType, inWitness);
162+
if (inWitness && !inRequirement)
163+
witnessType = adjustedWitnessType;
164+
};
165+
166+
applyAdjustment(TypeAdjustment::NoescapeToEscaping);
167+
applyAdjustment(TypeAdjustment::NonsendableToSendable);
152168

153169
// For @objc protocols, deal with differences in the optionality.
154170
// FIXME: It probably makes sense to extend this to non-@objc

lib/Sema/TypeCheckProtocol.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,12 +1136,19 @@ matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
11361136
AssociatedTypeDecl *getReferencedAssocTypeOfProtocol(Type type,
11371137
ProtocolDecl *proto);
11381138

1139+
enum class TypeAdjustment : uint8_t {
1140+
NoescapeToEscaping, NonsendableToSendable
1141+
};
1142+
11391143
/// Perform any necessary adjustments to the inferred associated type to
11401144
/// make it suitable for later use.
11411145
///
1142-
/// \param noescapeToEscaping Will be set \c true if this operation performed
1143-
/// the noescape-to-escaping adjustment.
1144-
Type adjustInferredAssociatedType(Type type, bool &noescapeToEscaping);
1146+
/// \param performed Will be set \c true if this operation performed
1147+
/// the adjustment, or \c false if the operation found a type that the
1148+
/// adjustment could have applied to but did not actually need to adjust it.
1149+
/// Unchanged otherwise.
1150+
Type adjustInferredAssociatedType(TypeAdjustment adjustment, Type type,
1151+
bool &performed);
11451152

11461153
/// Find the @objc requirement that are witnessed by the given
11471154
/// declaration.

0 commit comments

Comments
 (0)