Skip to content

Commit c1214fe

Browse files
committed
Sema: Three fixes for the new @escaping attribute
- If a parameter type is a sugared function type, mark the type as non-escaping by default. Previously, we were only doing this if the parameter type was written as a function type, with no additional sugar. This means in the following cases, the function parameter type is now non-escaping: func foo(f: ((Int) -> Void)) typealias Fn = (Int) -> Void func foo(f: Fn) - Also, allow @escaping to be used in the above cases: func foo(f: @escaping ((Int) -> Void)) typealias Fn = (Int) -> Void func foo(f: @escaping Fn) - Diagnose usages of @escaping in inappropriate locations, instead of just ignoring them. It is unfortunate that sometimes we end up desugaring the typealias, but currently there are other cases where this occurs too, such as qualified lookup of protocol typealiases with a concrete base type, and generic type aliases. A more general representation for sugared types (such as an AttributedType sugared type) would allow us to solve this in a more satisfactory manner in the future. However at the very least this patch factors out the common code paths and adds comments, so it shouldn't be too bad going forward. Note that this is a source-breaking change, both because @escaping might need to be added to parameters with a sugared function type, and @escaping might be removed if it appears somewhere where we do not mark function types as non-escaping by default. Fixes <https://bugs.swift.org/browse/SR-2053> and <https://bugs.swift.org/browse/SR-2397>.
1 parent ade019d commit c1214fe

21 files changed

+190
-119
lines changed

lib/Sema/TypeCheckType.cpp

Lines changed: 105 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,6 +1402,38 @@ static bool diagnoseAvailability(Type ty, IdentTypeRepr *IdType, SourceLoc Loc,
14021402
return false;
14031403
}
14041404

1405+
/// Whether the given DC is a noescape-by-default context, i.e. not a property
1406+
/// setter
1407+
static bool isDefaultNoEscapeContext(const DeclContext *DC) {
1408+
auto funcDecl = dyn_cast<FuncDecl>(DC);
1409+
return !funcDecl || !funcDecl->isSetter();
1410+
}
1411+
1412+
// Hack to apply context-specific @escaping to an AST function type.
1413+
static Type adjustFunctionExtInfo(DeclContext *DC,
1414+
Type ty,
1415+
TypeResolutionOptions options) {
1416+
// Remember whether this is a function parameter.
1417+
bool isFunctionParam =
1418+
options.contains(TR_FunctionInput) ||
1419+
options.contains(TR_ImmediateFunctionInput);
1420+
1421+
bool defaultNoEscape = isFunctionParam && isDefaultNoEscapeContext(DC);
1422+
1423+
// Desugar here
1424+
auto *funcTy = ty->castTo<FunctionType>();
1425+
auto extInfo = funcTy->getExtInfo();
1426+
if (defaultNoEscape && !extInfo.isNoEscape()) {
1427+
extInfo = extInfo.withNoEscape();
1428+
1429+
// We lost the sugar to flip the isNoEscape bit
1430+
return FunctionType::get(funcTy->getInput(), funcTy->getResult(), extInfo);
1431+
}
1432+
1433+
// Note: original sugared type
1434+
return ty;
1435+
}
1436+
14051437
/// \brief Returns a valid type or ErrorType in case of an error.
14061438
Type TypeChecker::resolveIdentifierType(
14071439
DeclContext *DC,
@@ -1432,6 +1464,11 @@ Type TypeChecker::resolveIdentifierType(
14321464
return ErrorType::get(Context);
14331465
}
14341466

1467+
// Hack to apply context-specific @escaping to a typealias with an underlying
1468+
// function type.
1469+
if (result->is<FunctionType>())
1470+
result = adjustFunctionExtInfo(DC, result, options);
1471+
14351472
// We allow a type to conform to a protocol that is less available than
14361473
// the type itself. This enables a type to retroactively model or directly
14371474
// conform to a protocol only available on newer OSes and yet still be used on
@@ -1637,29 +1674,20 @@ Type TypeChecker::resolveType(TypeRepr *TyR, DeclContext *DC,
16371674
return result;
16381675
}
16391676

1640-
/// Whether the given DC is a noescape-by-default context, i.e. not a property
1641-
/// setter
1642-
static bool isDefaultNoEscapeContext(const DeclContext *DC) {
1643-
auto funcDecl = dyn_cast<FuncDecl>(DC);
1644-
return !funcDecl || !funcDecl->isSetter();
1645-
}
1646-
16471677
Type TypeResolver::resolveType(TypeRepr *repr, TypeResolutionOptions options) {
16481678
assert(repr && "Cannot validate null TypeReprs!");
16491679

16501680
// If we know the type representation is invalid, just return an
16511681
// error type.
16521682
if (repr->isInvalid()) return ErrorType::get(TC.Context);
16531683

1654-
// Remember whether this is a function parameter.
1655-
bool isFunctionParam =
1656-
options.contains(TR_FunctionInput) ||
1657-
options.contains(TR_ImmediateFunctionInput);
1658-
16591684
// Strip the "is function input" bits unless this is a type that knows about
16601685
// them.
1661-
if (!isa<InOutTypeRepr>(repr) && !isa<TupleTypeRepr>(repr) &&
1662-
!isa<AttributedTypeRepr>(repr)) {
1686+
if (!isa<InOutTypeRepr>(repr) &&
1687+
!isa<TupleTypeRepr>(repr) &&
1688+
!isa<AttributedTypeRepr>(repr) &&
1689+
!isa<FunctionTypeRepr>(repr) &&
1690+
!isa<IdentTypeRepr>(repr)) {
16631691
options -= TR_ImmediateFunctionInput;
16641692
options -= TR_FunctionInput;
16651693
}
@@ -1686,11 +1714,10 @@ Type TypeResolver::resolveType(TypeRepr *repr, TypeResolutionOptions options) {
16861714
case TypeReprKind::Function:
16871715
if (!(options & TR_SILType)) {
16881716
// Default non-escaping for closure parameters
1689-
auto info = AnyFunctionType::ExtInfo().withNoEscape(
1690-
isFunctionParam &&
1691-
isDefaultNoEscapeContext(DC));
1692-
return resolveASTFunctionType(cast<FunctionTypeRepr>(repr), options,
1693-
info);
1717+
auto result = resolveASTFunctionType(cast<FunctionTypeRepr>(repr), options);
1718+
if (result && result->is<FunctionType>())
1719+
return adjustFunctionExtInfo(DC, result, options);
1720+
return result;
16941721
}
16951722
return resolveSILFunctionType(cast<FunctionTypeRepr>(repr), options);
16961723

@@ -1747,8 +1774,6 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
17471774
bool isFunctionParam =
17481775
options.contains(TR_FunctionInput) ||
17491776
options.contains(TR_ImmediateFunctionInput);
1750-
options -= TR_ImmediateFunctionInput;
1751-
options -= TR_FunctionInput;
17521777

17531778
// The type we're working with, in case we want to build it differently
17541779
// based on the attributes we see.
@@ -1772,7 +1797,11 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
17721797
if (base) {
17731798
Optional<MetatypeRepresentation> storedRepr;
17741799
// The instance type is not a SIL type.
1775-
auto instanceOptions = options - TR_SILType;
1800+
auto instanceOptions = options;
1801+
instanceOptions -= TR_SILType;
1802+
instanceOptions -= TR_ImmediateFunctionInput;
1803+
instanceOptions -= TR_FunctionInput;
1804+
17761805
auto instanceTy = resolveType(base, instanceOptions);
17771806
if (!instanceTy || instanceTy->is<ErrorType>())
17781807
return instanceTy;
@@ -1901,10 +1930,6 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
19011930

19021931
ty = resolveSILFunctionType(fnRepr, options, extInfo, calleeConvention);
19031932
if (!ty || ty->is<ErrorType>()) return ty;
1904-
1905-
for (auto i : FunctionAttrs)
1906-
attrs.clearAttribute(i);
1907-
attrs.convention = None;
19081933
} else if (hasFunctionAttr && fnRepr) {
19091934

19101935
FunctionType::Representation rep = FunctionType::Representation::Swift;
@@ -1948,29 +1973,54 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
19481973
.fixItReplace(resultRange, "Never");
19491974
}
19501975

1951-
bool defaultNoEscape = false;
1952-
if (isFunctionParam && !attrs.has(TAK_escaping)) {
1953-
defaultNoEscape = isDefaultNoEscapeContext(DC);
1954-
}
1955-
1956-
if (isFunctionParam && attrs.has(TAK_noescape) &&
1957-
isDefaultNoEscapeContext(DC)) {
1976+
if (attrs.has(TAK_noescape)) {
19581977
// FIXME: diagnostic to tell user this is redundant and drop it
19591978
}
19601979

19611980
// Resolve the function type directly with these attributes.
19621981
FunctionType::ExtInfo extInfo(rep,
19631982
attrs.has(TAK_autoclosure),
1964-
defaultNoEscape | attrs.has(TAK_noescape),
1983+
attrs.has(TAK_noescape),
19651984
fnRepr->throws());
19661985

19671986
ty = resolveASTFunctionType(fnRepr, options, extInfo);
19681987
if (!ty || ty->is<ErrorType>()) return ty;
1988+
}
19691989

1970-
for (auto i : FunctionAttrs)
1971-
attrs.clearAttribute(i);
1972-
attrs.convention = None;
1973-
} else if (hasFunctionAttr) {
1990+
auto instanceOptions = options;
1991+
instanceOptions -= TR_ImmediateFunctionInput;
1992+
instanceOptions -= TR_FunctionInput;
1993+
1994+
// If we didn't build the type differently above, we might have
1995+
// a typealias pointing at a function type with the @escaping
1996+
// attribute. Resolve the type as if it were in non-parameter
1997+
// context, and then set isNoEscape if @escaping is not present.
1998+
if (!ty) ty = resolveType(repr, instanceOptions);
1999+
if (!ty || ty->is<ErrorType>()) return ty;
2000+
2001+
// Handle @escaping
2002+
if (hasFunctionAttr && ty->is<FunctionType>()) {
2003+
if (attrs.has(TAK_escaping)) {
2004+
// The attribute is meaningless except on parameter types.
2005+
if (!isFunctionParam) {
2006+
auto &SM = TC.Context.SourceMgr;
2007+
auto loc = attrs.getLoc(TAK_escaping);
2008+
auto attrRange = SourceRange(
2009+
loc.getAdvancedLoc(-1),
2010+
Lexer::getLocForEndOfToken(SM, loc));
2011+
2012+
TC.diagnose(loc, diag::escaping_function_type)
2013+
.fixItRemove(attrRange);
2014+
}
2015+
2016+
attrs.clearAttribute(TAK_escaping);
2017+
} else {
2018+
// No attribute; set the isNoEscape bit if we're in parameter context.
2019+
ty = adjustFunctionExtInfo(DC, ty, options);
2020+
}
2021+
}
2022+
2023+
if (hasFunctionAttr && !fnRepr) {
19742024
// @autoclosure usually auto-implies @noescape, don't complain about both
19752025
// of them.
19762026
if (attrs.has(TAK_autoclosure))
@@ -1983,11 +2033,11 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
19832033
attrs.clearAttribute(i);
19842034
}
19852035
}
1986-
}
1987-
1988-
// If we didn't build the type differently above, build it normally now.
1989-
if (!ty) ty = resolveType(repr, options);
1990-
if (!ty || ty->is<ErrorType>()) return ty;
2036+
} else if (hasFunctionAttr && fnRepr) {
2037+
for (auto i : FunctionAttrs)
2038+
attrs.clearAttribute(i);
2039+
attrs.convention = None;
2040+
}
19912041

19922042
// In SIL, handle @opened (n), which creates an existential archetype.
19932043
if (attrs.has(TAK_opened)) {
@@ -2036,6 +2086,9 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
20362086
Type TypeResolver::resolveASTFunctionType(FunctionTypeRepr *repr,
20372087
TypeResolutionOptions options,
20382088
FunctionType::ExtInfo extInfo) {
2089+
options -= TR_ImmediateFunctionInput;
2090+
options -= TR_FunctionInput;
2091+
20392092
Type inputTy = resolveType(repr->getArgsTypeRepr(),
20402093
options | TR_ImmediateFunctionInput);
20412094
if (!inputTy || inputTy->is<ErrorType>()) return inputTy;
@@ -2100,6 +2153,9 @@ Type TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr,
21002153
TypeResolutionOptions options,
21012154
SILFunctionType::ExtInfo extInfo,
21022155
ParameterConvention callee) {
2156+
options -= TR_ImmediateFunctionInput;
2157+
options -= TR_FunctionInput;
2158+
21032159
bool hasError = false;
21042160

21052161
SmallVector<SILParameterInfo, 4> params;
@@ -2428,9 +2484,12 @@ Type TypeResolver::resolveTupleType(TupleTypeRepr *repr,
24282484

24292485
// If this is the top level of a function input list, peel off the
24302486
// ImmediateFunctionInput marker and install a FunctionInput one instead.
2431-
auto elementOptions = withoutContext(options);
2432-
if (options & TR_ImmediateFunctionInput)
2433-
elementOptions |= TR_FunctionInput;
2487+
auto elementOptions = options;
2488+
if (!repr->isParenType()) {
2489+
elementOptions = withoutContext(elementOptions);
2490+
if (options & TR_ImmediateFunctionInput)
2491+
elementOptions |= TR_FunctionInput;
2492+
}
24342493

24352494
for (auto tyR : repr->getElements()) {
24362495
NamedTypeRepr *namedTyR = dyn_cast<NamedTypeRepr>(tyR);

lib/Serialization/Serialization.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3097,9 +3097,9 @@ void Serializer::writeType(Type ty) {
30973097
}
30983098

30993099
case TypeKind::Optional: {
3100-
auto sliceTy = cast<OptionalType>(ty.getPointer());
3100+
auto optionalTy = cast<OptionalType>(ty.getPointer());
31013101

3102-
Type base = sliceTy->getBaseType();
3102+
Type base = optionalTy->getBaseType();
31033103

31043104
unsigned abbrCode = DeclTypeAbbrCodes[OptionalTypeLayout::Code];
31053105
OptionalTypeLayout::emitRecord(Out, ScratchRecord, abbrCode,
@@ -3108,13 +3108,13 @@ void Serializer::writeType(Type ty) {
31083108
}
31093109

31103110
case TypeKind::ImplicitlyUnwrappedOptional: {
3111-
auto sliceTy = cast<ImplicitlyUnwrappedOptionalType>(ty.getPointer());
3111+
auto optionalTy = cast<ImplicitlyUnwrappedOptionalType>(ty.getPointer());
31123112

3113-
Type base = sliceTy->getBaseType();
3113+
Type base = optionalTy->getBaseType();
31143114

31153115
unsigned abbrCode = DeclTypeAbbrCodes[ImplicitlyUnwrappedOptionalTypeLayout::Code];
31163116
ImplicitlyUnwrappedOptionalTypeLayout::emitRecord(Out, ScratchRecord, abbrCode,
3117-
addTypeRef(base));
3117+
addTypeRef(base));
31183118
break;
31193119
}
31203120

stdlib/private/StdlibCollectionUnittest/CheckCollectionType.swift.gyb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,8 +511,8 @@ internal func _product<C1 : Collection, C2 : Collection>(
511511
wrapValueIntoEquatable: @escaping (
512512
MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element,
513513

514-
extractValueFromEquatable:
515-
((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
514+
extractValueFromEquatable: @escaping (
515+
CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue,
516516

517517
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
518518
outOfBoundsIndexOffset: Int = 1,

stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift.gyb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,11 @@ extension TestSuite {
9292

9393
makeCollectionOfEquatable: @escaping ([CollectionWithEquatableElement.Iterator.Element]) -> CollectionWithEquatableElement,
9494
wrapValueIntoEquatable: @escaping (MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element,
95-
extractValueFromEquatable: ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
95+
extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
9696

9797
makeCollectionOfComparable: @escaping ([CollectionWithComparableElement.Iterator.Element]) -> CollectionWithComparableElement,
9898
wrapValueIntoComparable: @escaping (MinimalComparableValue) -> CollectionWithComparableElement.Iterator.Element,
99-
extractValueFromComparable: ((CollectionWithComparableElement.Iterator.Element) -> MinimalComparableValue),
99+
extractValueFromComparable: @escaping ((CollectionWithComparableElement.Iterator.Element) -> MinimalComparableValue),
100100

101101
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
102102
outOfBoundsIndexOffset: Int = 1,
@@ -502,8 +502,8 @@ self.test("\(testNamePrefix).sorted/DispatchesThrough_withUnsafeMutableBufferPoi
502502

503503
func checkSort_${'Predicate' if predicate else 'WhereElementIsComparable'}(
504504
sequence: [Int],
505-
equalImpl: ((Int, Int) -> Bool),
506-
lessImpl: ((Int, Int) -> Bool),
505+
equalImpl: @escaping ((Int, Int) -> Bool),
506+
lessImpl: @escaping ((Int, Int) -> Bool),
507507
verifyOrder: Bool
508508
) {
509509
% if predicate:
@@ -683,11 +683,11 @@ self.test("\(testNamePrefix).partition/InvalidOrderings") {
683683

684684
makeCollectionOfEquatable: @escaping ([CollectionWithEquatableElement.Iterator.Element]) -> CollectionWithEquatableElement,
685685
wrapValueIntoEquatable: @escaping (MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element,
686-
extractValueFromEquatable: ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
686+
extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
687687

688688
makeCollectionOfComparable: @escaping ([CollectionWithComparableElement.Iterator.Element]) -> CollectionWithComparableElement,
689689
wrapValueIntoComparable: @escaping (MinimalComparableValue) -> CollectionWithComparableElement.Iterator.Element,
690-
extractValueFromComparable: ((CollectionWithComparableElement.Iterator.Element) -> MinimalComparableValue),
690+
extractValueFromComparable: @escaping ((CollectionWithComparableElement.Iterator.Element) -> MinimalComparableValue),
691691

692692
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
693693
outOfBoundsIndexOffset: Int = 1,
@@ -836,11 +836,11 @@ self.test("\(testNamePrefix).partition/DispatchesThrough_withUnsafeMutableBuffer
836836

837837
makeCollectionOfEquatable: @escaping ([CollectionWithEquatableElement.Iterator.Element]) -> CollectionWithEquatableElement,
838838
wrapValueIntoEquatable: @escaping (MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element,
839-
extractValueFromEquatable: ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
839+
extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
840840

841841
makeCollectionOfComparable: @escaping ([CollectionWithComparableElement.Iterator.Element]) -> CollectionWithComparableElement,
842842
wrapValueIntoComparable: @escaping (MinimalComparableValue) -> CollectionWithComparableElement.Iterator.Element,
843-
extractValueFromComparable: ((CollectionWithComparableElement.Iterator.Element) -> MinimalComparableValue),
843+
extractValueFromComparable: @escaping ((CollectionWithComparableElement.Iterator.Element) -> MinimalComparableValue),
844844

845845
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
846846
outOfBoundsIndexOffset: Int = 1,
@@ -919,8 +919,8 @@ self.test("\(testNamePrefix).partition/DispatchesThrough_withUnsafeMutableBuffer
919919

920920
func checkSortInPlace_${'Predicate' if predicate else 'WhereElementIsComparable'}(
921921
sequence: [Int],
922-
equalImpl: ((Int, Int) -> Bool),
923-
lessImpl: ((Int, Int) -> Bool),
922+
equalImpl: @escaping ((Int, Int) -> Bool),
923+
lessImpl: @escaping ((Int, Int) -> Bool),
924924
verifyOrder: Bool
925925
) {
926926
% if predicate:

stdlib/private/StdlibCollectionUnittest/CheckRangeReplaceableCollectionType.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ extension TestSuite {
456456

457457
makeCollectionOfEquatable: @escaping ([CollectionWithEquatableElement.Iterator.Element]) -> CollectionWithEquatableElement,
458458
wrapValueIntoEquatable: @escaping (MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element,
459-
extractValueFromEquatable: ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
459+
extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
460460

461461
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
462462
outOfBoundsIndexOffset: Int = 1,
@@ -1168,7 +1168,7 @@ self.test("\(testNamePrefix).OperatorPlus") {
11681168

11691169
makeCollectionOfEquatable: @escaping ([CollectionWithEquatableElement.Iterator.Element]) -> CollectionWithEquatableElement,
11701170
wrapValueIntoEquatable: @escaping (MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element,
1171-
extractValueFromEquatable: ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
1171+
extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
11721172

11731173
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
11741174
outOfBoundsIndexOffset: Int = 1
@@ -1297,7 +1297,7 @@ self.test("\(testNamePrefix).removeLast(n: Int)/whereIndexIsBidirectional/remove
12971297

12981298
makeCollectionOfEquatable: @escaping ([CollectionWithEquatableElement.Iterator.Element]) -> CollectionWithEquatableElement,
12991299
wrapValueIntoEquatable: @escaping (MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element,
1300-
extractValueFromEquatable: ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
1300+
extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
13011301

13021302
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
13031303
outOfBoundsIndexOffset: Int = 1

0 commit comments

Comments
 (0)