Skip to content

Commit 3a070b6

Browse files
authored
Merge pull request #4347 from slavapestov/noescape-by-default-typealias-fix
Sema: Apply @escaping to typealiases with underlying function type
2 parents 73dbfcb + 79a1512 commit 3a070b6

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->is<FunctionType>())
1719+
return adjustFunctionExtInfo(DC, result, options);
1720+
return result;
16941721
}
16951722
return resolveSILFunctionType(cast<FunctionTypeRepr>(repr), options);
16961723

@@ -1761,8 +1788,6 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
17611788
bool isFunctionParam =
17621789
options.contains(TR_FunctionInput) ||
17631790
options.contains(TR_ImmediateFunctionInput);
1764-
options -= TR_ImmediateFunctionInput;
1765-
options -= TR_FunctionInput;
17661791

17671792
// The type we're working with, in case we want to build it differently
17681793
// based on the attributes we see.
@@ -1786,7 +1811,11 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
17861811
if (base) {
17871812
Optional<MetatypeRepresentation> storedRepr;
17881813
// The instance type is not a SIL type.
1789-
auto instanceOptions = options - TR_SILType;
1814+
auto instanceOptions = options;
1815+
instanceOptions -= TR_SILType;
1816+
instanceOptions -= TR_ImmediateFunctionInput;
1817+
instanceOptions -= TR_FunctionInput;
1818+
17901819
auto instanceTy = resolveType(base, instanceOptions);
17911820
if (!instanceTy || instanceTy->is<ErrorType>())
17921821
return instanceTy;
@@ -1915,10 +1944,6 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
19151944

19161945
ty = resolveSILFunctionType(fnRepr, options, extInfo, calleeConvention);
19171946
if (!ty || ty->is<ErrorType>()) return ty;
1918-
1919-
for (auto i : FunctionAttrs)
1920-
attrs.clearAttribute(i);
1921-
attrs.convention = None;
19221947
} else if (hasFunctionAttr && fnRepr) {
19231948

19241949
FunctionType::Representation rep = FunctionType::Representation::Swift;
@@ -1962,29 +1987,54 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
19621987
.fixItReplace(resultRange, "Never");
19631988
}
19641989

1965-
bool defaultNoEscape = false;
1966-
if (isFunctionParam && !attrs.has(TAK_escaping)) {
1967-
defaultNoEscape = isDefaultNoEscapeContext(DC);
1968-
}
1969-
1970-
if (isFunctionParam && attrs.has(TAK_noescape) &&
1971-
isDefaultNoEscapeContext(DC)) {
1990+
if (attrs.has(TAK_noescape)) {
19721991
// FIXME: diagnostic to tell user this is redundant and drop it
19731992
}
19741993

19751994
// Resolve the function type directly with these attributes.
19761995
FunctionType::ExtInfo extInfo(rep,
19771996
attrs.has(TAK_autoclosure),
1978-
defaultNoEscape | attrs.has(TAK_noescape),
1997+
attrs.has(TAK_noescape),
19791998
fnRepr->throws());
19801999

19812000
ty = resolveASTFunctionType(fnRepr, options, extInfo);
19822001
if (!ty || ty->is<ErrorType>()) return ty;
2002+
}
19832003

1984-
for (auto i : FunctionAttrs)
1985-
attrs.clearAttribute(i);
1986-
attrs.convention = None;
1987-
} else if (hasFunctionAttr) {
2004+
auto instanceOptions = options;
2005+
instanceOptions -= TR_ImmediateFunctionInput;
2006+
instanceOptions -= TR_FunctionInput;
2007+
2008+
// If we didn't build the type differently above, we might have
2009+
// a typealias pointing at a function type with the @escaping
2010+
// attribute. Resolve the type as if it were in non-parameter
2011+
// context, and then set isNoEscape if @escaping is not present.
2012+
if (!ty) ty = resolveType(repr, instanceOptions);
2013+
if (!ty || ty->is<ErrorType>()) return ty;
2014+
2015+
// Handle @escaping
2016+
if (hasFunctionAttr && ty->is<FunctionType>()) {
2017+
if (attrs.has(TAK_escaping)) {
2018+
// The attribute is meaningless except on parameter types.
2019+
if (!isFunctionParam) {
2020+
auto &SM = TC.Context.SourceMgr;
2021+
auto loc = attrs.getLoc(TAK_escaping);
2022+
auto attrRange = SourceRange(
2023+
loc.getAdvancedLoc(-1),
2024+
Lexer::getLocForEndOfToken(SM, loc));
2025+
2026+
TC.diagnose(loc, diag::escaping_function_type)
2027+
.fixItRemove(attrRange);
2028+
}
2029+
2030+
attrs.clearAttribute(TAK_escaping);
2031+
} else {
2032+
// No attribute; set the isNoEscape bit if we're in parameter context.
2033+
ty = adjustFunctionExtInfo(DC, ty, options);
2034+
}
2035+
}
2036+
2037+
if (hasFunctionAttr && !fnRepr) {
19882038
// @autoclosure usually auto-implies @noescape, don't complain about both
19892039
// of them.
19902040
if (attrs.has(TAK_autoclosure))
@@ -1997,11 +2047,11 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
19972047
attrs.clearAttribute(i);
19982048
}
19992049
}
2000-
}
2001-
2002-
// If we didn't build the type differently above, build it normally now.
2003-
if (!ty) ty = resolveType(repr, options);
2004-
if (!ty || ty->is<ErrorType>()) return ty;
2050+
} else if (hasFunctionAttr && fnRepr) {
2051+
for (auto i : FunctionAttrs)
2052+
attrs.clearAttribute(i);
2053+
attrs.convention = None;
2054+
}
20052055

20062056
// In SIL, handle @opened (n), which creates an existential archetype.
20072057
if (attrs.has(TAK_opened)) {
@@ -2056,6 +2106,9 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
20562106
Type TypeResolver::resolveASTFunctionType(FunctionTypeRepr *repr,
20572107
TypeResolutionOptions options,
20582108
FunctionType::ExtInfo extInfo) {
2109+
options -= TR_ImmediateFunctionInput;
2110+
options -= TR_FunctionInput;
2111+
20592112
Type inputTy = resolveType(repr->getArgsTypeRepr(),
20602113
options | TR_ImmediateFunctionInput);
20612114
if (!inputTy || inputTy->is<ErrorType>()) return inputTy;
@@ -2120,6 +2173,9 @@ Type TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr,
21202173
TypeResolutionOptions options,
21212174
SILFunctionType::ExtInfo extInfo,
21222175
ParameterConvention callee) {
2176+
options -= TR_ImmediateFunctionInput;
2177+
options -= TR_FunctionInput;
2178+
21232179
bool hasError = false;
21242180

21252181
SmallVector<SILParameterInfo, 4> params;
@@ -2448,9 +2504,12 @@ Type TypeResolver::resolveTupleType(TupleTypeRepr *repr,
24482504

24492505
// If this is the top level of a function input list, peel off the
24502506
// ImmediateFunctionInput marker and install a FunctionInput one instead.
2451-
auto elementOptions = withoutContext(options);
2452-
if (options & TR_ImmediateFunctionInput)
2453-
elementOptions |= TR_FunctionInput;
2507+
auto elementOptions = options;
2508+
if (!repr->isParenType()) {
2509+
elementOptions = withoutContext(elementOptions);
2510+
if (options & TR_ImmediateFunctionInput)
2511+
elementOptions |= TR_FunctionInput;
2512+
}
24542513

24552514
for (auto tyR : repr->getElements()) {
24562515
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
@@ -514,8 +514,8 @@ internal func _product<C1 : Collection, C2 : Collection>(
514514
wrapValueIntoEquatable: @escaping (
515515
MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element,
516516

517-
extractValueFromEquatable:
518-
((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
517+
extractValueFromEquatable: @escaping (
518+
CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue,
519519

520520
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
521521
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,
@@ -505,8 +505,8 @@ self.test("\(testNamePrefix).sorted/DispatchesThrough_withUnsafeMutableBufferPoi
505505

506506
func checkSort_${'Predicate' if predicate else 'WhereElementIsComparable'}(
507507
sequence: [Int],
508-
equalImpl: ((Int, Int) -> Bool),
509-
lessImpl: ((Int, Int) -> Bool),
508+
equalImpl: @escaping ((Int, Int) -> Bool),
509+
lessImpl: @escaping ((Int, Int) -> Bool),
510510
verifyOrder: Bool
511511
) {
512512
% if predicate:
@@ -686,11 +686,11 @@ self.test("\(testNamePrefix).partition/InvalidOrderings") {
686686

687687
makeCollectionOfEquatable: @escaping ([CollectionWithEquatableElement.Iterator.Element]) -> CollectionWithEquatableElement,
688688
wrapValueIntoEquatable: @escaping (MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element,
689-
extractValueFromEquatable: ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
689+
extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
690690

691691
makeCollectionOfComparable: @escaping ([CollectionWithComparableElement.Iterator.Element]) -> CollectionWithComparableElement,
692692
wrapValueIntoComparable: @escaping (MinimalComparableValue) -> CollectionWithComparableElement.Iterator.Element,
693-
extractValueFromComparable: ((CollectionWithComparableElement.Iterator.Element) -> MinimalComparableValue),
693+
extractValueFromComparable: @escaping ((CollectionWithComparableElement.Iterator.Element) -> MinimalComparableValue),
694694

695695
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
696696
outOfBoundsIndexOffset: Int = 1,
@@ -842,11 +842,11 @@ self.test("\(testNamePrefix).partition/DispatchesThrough_withUnsafeMutableBuffer
842842

843843
makeCollectionOfEquatable: @escaping ([CollectionWithEquatableElement.Iterator.Element]) -> CollectionWithEquatableElement,
844844
wrapValueIntoEquatable: @escaping (MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element,
845-
extractValueFromEquatable: ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
845+
extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
846846

847847
makeCollectionOfComparable: @escaping ([CollectionWithComparableElement.Iterator.Element]) -> CollectionWithComparableElement,
848848
wrapValueIntoComparable: @escaping (MinimalComparableValue) -> CollectionWithComparableElement.Iterator.Element,
849-
extractValueFromComparable: ((CollectionWithComparableElement.Iterator.Element) -> MinimalComparableValue),
849+
extractValueFromComparable: @escaping ((CollectionWithComparableElement.Iterator.Element) -> MinimalComparableValue),
850850

851851
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
852852
outOfBoundsIndexOffset: Int = 1,
@@ -928,8 +928,8 @@ self.test("\(testNamePrefix).partition/DispatchesThrough_withUnsafeMutableBuffer
928928

929929
func checkSortInPlace_${'Predicate' if predicate else 'WhereElementIsComparable'}(
930930
sequence: [Int],
931-
equalImpl: ((Int, Int) -> Bool),
932-
lessImpl: ((Int, Int) -> Bool),
931+
equalImpl: @escaping ((Int, Int) -> Bool),
932+
lessImpl: @escaping ((Int, Int) -> Bool),
933933
verifyOrder: Bool
934934
) {
935935
% 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,
@@ -1171,7 +1171,7 @@ self.test("\(testNamePrefix).OperatorPlus") {
11711171

11721172
makeCollectionOfEquatable: @escaping ([CollectionWithEquatableElement.Iterator.Element]) -> CollectionWithEquatableElement,
11731173
wrapValueIntoEquatable: @escaping (MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element,
1174-
extractValueFromEquatable: ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
1174+
extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
11751175

11761176
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
11771177
outOfBoundsIndexOffset: Int = 1
@@ -1303,7 +1303,7 @@ self.test("\(testNamePrefix).removeLast(n: Int)/whereIndexIsBidirectional/remove
13031303

13041304
makeCollectionOfEquatable: @escaping ([CollectionWithEquatableElement.Iterator.Element]) -> CollectionWithEquatableElement,
13051305
wrapValueIntoEquatable: @escaping (MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element,
1306-
extractValueFromEquatable: ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
1306+
extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue),
13071307

13081308
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
13091309
outOfBoundsIndexOffset: Int = 1

0 commit comments

Comments
 (0)