Skip to content

Commit 8219d4f

Browse files
authored
Merge pull request #3853 from milseman/noescape_by_default
SE-0103 Noescape by default
2 parents e37d048 + 25ac879 commit 8219d4f

File tree

106 files changed

+1611
-1486
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+1611
-1486
lines changed

benchmark/single-source/Integrate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class Integrate {
2020

2121
let fun: (Double) -> Double
2222

23-
init (f: (Double) -> Double) {
23+
init (f: @escaping (Double) -> Double) {
2424
fun = f
2525
}
2626

benchmark/utils/DriverUtils.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ struct Test {
5050
let index: Int
5151
let f: (Int) -> ()
5252
var run: Bool
53-
init(name: String, n: Int, f: (Int) -> ()) {
53+
init(name: String, n: Int, f: @escaping (Int) -> ()) {
5454
self.name = name
5555
self.index = n
5656
self.f = f

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1821,6 +1821,8 @@ ERROR(autoclosure_function_type,none,
18211821
())
18221822
ERROR(autoclosure_function_input_nonunit,none,
18231823
"autoclosure argument type must be '()'", ())
1824+
1825+
// FIXME: drop these when we drop @noescape
18241826
ERROR(noescape_function_type,none,
18251827
"@noescape may only be applied to parameters of function type",
18261828
())
@@ -1829,6 +1831,7 @@ ERROR(noescape_implied_by_autoclosure,none,
18291831
"redundantly specified", ())
18301832
ERROR(noescape_conflicts_escaping_autoclosure,none,
18311833
"@noescape conflicts with @autoclosure(escaping)", ())
1834+
18321835
ERROR(escaping_function_type,none,
18331836
"@escaping may only be applied to parameters of function type", ())
18341837

@@ -2271,17 +2274,20 @@ WARNING(optional_pattern_match_promotion,none,
22712274
ERROR(type_of_metatype,none,
22722275
"'.dynamicType' is not allowed after a type name", ())
22732276
ERROR(invalid_noescape_use,none,
2274-
"@noescape %select{value|parameter}1 %0 may only be called",
2277+
"non-escaping %select{value|parameter}1 %0 may only be called",
22752278
(Identifier, bool))
22762279
NOTE(noescape_autoclosure,none,
2277-
"parameter %0 is implicitly @noescape because it was declared @autoclosure",
2280+
"parameter %0 is implicitly non-escaping because it was declared @autoclosure",
2281+
(Identifier))
2282+
NOTE(noescape_parameter,none,
2283+
"parameter %0 is implicitly non-escaping",
22782284
(Identifier))
22792285

22802286
ERROR(closure_noescape_use,none,
2281-
"closure use of @noescape parameter %0 may allow it to escape",
2287+
"closure use of non-escaping parameter %0 may allow it to escape",
22822288
(Identifier))
22832289
ERROR(decl_closure_noescape_use,none,
2284-
"declaration closing over @noescape parameter %0 may allow it to escape",
2290+
"declaration closing over non-escaping parameter %0 may allow it to escape",
22852291
(Identifier))
22862292

22872293
ERROR(capture_across_type_decl,none,

include/swift/AST/PrintOptions.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,12 @@ struct PrintOptions {
261261
/// Whether we are printing part of SIL body.
262262
bool PrintInSILBody = false;
263263

264+
/// Whether to print the types as if they appear as function parameters. This
265+
/// governs whether we print a function type with an explicit @escaping. This
266+
/// is also set and restored internally when visiting a type in a parameter
267+
/// position.
268+
bool PrintAsInParamType = false;
269+
264270
/// Whether to use an empty line to separate two members in a single decl.
265271
bool EmptyLineBetweenMembers = false;
266272

include/swift/AST/Types.h

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2132,7 +2132,6 @@ class AnyFunctionType : public TypeBase {
21322132
enum : uint16_t { AutoClosureMask = 0x010 };
21332133
enum : uint16_t { NoEscapeMask = 0x020 };
21342134
enum : uint16_t { ThrowsMask = 0x040 };
2135-
enum : uint16_t { ExplicitlyEscapingMask = 0x080 };
21362135

21372136
uint16_t Bits;
21382137

@@ -2153,17 +2152,15 @@ class AnyFunctionType : public TypeBase {
21532152

21542153
// Constructor with no defaults.
21552154
ExtInfo(Representation Rep,
2156-
bool IsAutoClosure, bool IsNoEscape, bool IsExplicitlyEscaping,
2155+
bool IsAutoClosure, bool IsNoEscape,
21572156
bool Throws)
21582157
: ExtInfo(Rep, Throws) {
21592158
Bits |= (IsAutoClosure ? AutoClosureMask : 0);
21602159
Bits |= (IsNoEscape ? NoEscapeMask : 0);
2161-
Bits |= (IsExplicitlyEscaping ? ExplicitlyEscapingMask : 0);
21622160
}
21632161

21642162
bool isAutoClosure() const { return Bits & AutoClosureMask; }
21652163
bool isNoEscape() const { return Bits & NoEscapeMask; }
2166-
bool isExplicitlyEscaping() const { return Bits & ExplicitlyEscapingMask; }
21672164
bool throws() const { return Bits & ThrowsMask; }
21682165
Representation getRepresentation() const {
21692166
unsigned rawRep = Bits & RepresentationMask;
@@ -2287,12 +2284,6 @@ class AnyFunctionType : public TypeBase {
22872284
return getExtInfo().isNoEscape();
22882285
}
22892286

2290-
/// \brief True if the parameter declaration it is attached to has explicitly
2291-
/// been marked with the @escaping attribute. This is a temporary measure.
2292-
bool isExplicitlyEscaping() const {
2293-
return getExtInfo().isExplicitlyEscaping();
2294-
}
2295-
22962287
bool throws() const {
22972288
return getExtInfo().throws();
22982289
}

include/swift/Serialization/ModuleFormat.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const uint16_t VERSION_MAJOR = 0;
5353
/// in source control, you should also update the comment to briefly
5454
/// describe what change you made. The content of this comment isn't important;
5555
/// it just ensures a conflict if two people change the module format.
56-
const uint16_t VERSION_MINOR = 258; // Last change: precedencegroup
56+
const uint16_t VERSION_MINOR = 259; // Last change: drop explicitlyEscaping
5757

5858
using DeclID = PointerEmbeddedInt<unsigned, 31>;
5959
using DeclIDField = BCFixed<31>;
@@ -590,7 +590,6 @@ namespace decls_block {
590590
FunctionTypeRepresentationField, // representation
591591
BCFixed<1>, // auto-closure?
592592
BCFixed<1>, // noescape?
593-
BCFixed<1>, // explicitlyEscaping?
594593
BCFixed<1> // throws?
595594
>;
596595

lib/AST/ASTDumper.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2916,8 +2916,11 @@ namespace {
29162916
}
29172917

29182918
printFlag(T->isAutoClosure(), "autoclosure");
2919-
printFlag(T->isNoEscape(), "noescape");
2920-
printFlag(T->isExplicitlyEscaping(), "escaping");
2919+
2920+
// Dump out either @noescape or @escaping
2921+
printFlag(T->isNoEscape(), "@noescape");
2922+
printFlag(!T->isNoEscape(), "@escaping");
2923+
29212924
printFlag(T->throws(), "throws");
29222925

29232926
printRec("input", T->getInput());

lib/AST/ASTPrinter.cpp

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2535,7 +2535,11 @@ void PrintAST::visitVarDecl(VarDecl *decl) {
25352535
}
25362536

25372537
void PrintAST::visitParamDecl(ParamDecl *decl) {
2538-
return visitVarDecl(decl);
2538+
// Set and restore in-parameter-position printing of types
2539+
auto prior = Options.PrintAsInParamType;
2540+
Options.PrintAsInParamType = true;
2541+
visitVarDecl(decl);
2542+
Options.PrintAsInParamType = prior;
25392543
}
25402544

25412545
void PrintAST::printOneParameter(const ParamDecl *param, bool Curried,
@@ -2589,7 +2593,11 @@ void PrintAST::printOneParameter(const ParamDecl *param, bool Curried,
25892593
TheTypeLoc.setType(BGT->getGenericArgs()[0]);
25902594
}
25912595

2596+
// Set and restore in-parameter-position printing of types
2597+
auto prior = Options.PrintAsInParamType;
2598+
Options.PrintAsInParamType = true;
25922599
printTypeLoc(TheTypeLoc);
2600+
Options.PrintAsInParamType = prior;
25932601

25942602
if (param->isVariadic())
25952603
Printer << "...";
@@ -3313,6 +3321,10 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
33133321
const PrintOptions &Options;
33143322
Optional<std::vector<GenericParamList *>> UnwrappedGenericParams;
33153323

3324+
/// Whether we are printing something in a function parameter position, and
3325+
/// thus want to print @escaping if it escapes.
3326+
bool inParameterPrinting;
3327+
33163328
void printDeclContext(DeclContext *DC) {
33173329
switch (DC->getContextKind()) {
33183330
case DeclContextKind::Module: {
@@ -3480,7 +3492,8 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
34803492

34813493
public:
34823494
TypePrinter(ASTPrinter &Printer, const PrintOptions &PO)
3483-
: Printer(Printer), Options(PO) {}
3495+
: Printer(Printer), Options(PO),
3496+
inParameterPrinting(Options.PrintAsInParamType) {}
34843497

34853498
void visit(Type T) {
34863499
Printer.printTypePre(TypeLoc::withoutLoc(T));
@@ -3745,11 +3758,10 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
37453758
Printer << "@autoclosure ";
37463759
else
37473760
Printer << "@autoclosure(escaping) ";
3748-
} else if (info.isNoEscape()) {
3749-
// autoclosure implies noescape.
3750-
Printer << "@noescape ";
3751-
} else if (info.isExplicitlyEscaping()) {
3752-
Printer << "@escaping ";
3761+
} else if (inParameterPrinting) {
3762+
if (!info.isNoEscape()) {
3763+
Printer << "@escaping ";
3764+
}
37533765
}
37543766

37553767
if (Options.PrintFunctionRepresentationAttrs) {
@@ -3841,8 +3853,17 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
38413853

38423854
if (needsParens)
38433855
Printer << "(";
3844-
3856+
3857+
// Set in-parameter-position printing to print our parameters, then unset it
3858+
// for the return type (in case it is also a function), and restore at the
3859+
// end.
3860+
auto prior = inParameterPrinting;
3861+
inParameterPrinting = true;
38453862
visit(inputType);
3863+
inParameterPrinting = false;
3864+
SWIFT_DEFER {
3865+
inParameterPrinting = prior;
3866+
};
38463867

38473868
if (needsParens)
38483869
Printer << ")";

lib/Sema/CSApply.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5587,7 +5587,6 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
55875587
swift::AnyFunctionType::ExtInfo newEI(fromEI.getRepresentation(),
55885588
toEI.isAutoClosure(),
55895589
toEI.isNoEscape() | fromEI.isNoEscape(),
5590-
toEI.isExplicitlyEscaping() | fromEI.isExplicitlyEscaping(),
55915590
toEI.throws() & fromEI.throws());
55925591
auto newToType = FunctionType::get(fromFunc->getInput(),
55935592
fromFunc->getResult(), newEI);

lib/Sema/MiscDiagnostics.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,16 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E,
464464

465465
TC.diagnose(DRE->getStartLoc(), diag::invalid_noescape_use,
466466
DRE->getDecl()->getName(), isa<ParamDecl>(DRE->getDecl()));
467-
if (AFT->isAutoClosure())
467+
468+
// If we're a parameter, emit a helpful fixit to add @escaping
469+
auto paramDecl = dyn_cast<ParamDecl>(DRE->getDecl());
470+
auto isAutoClosure = AFT->isAutoClosure();
471+
if (paramDecl && !isAutoClosure) {
472+
TC.diagnose(paramDecl->getStartLoc(), diag::noescape_parameter,
473+
paramDecl->getName())
474+
.fixItInsert(paramDecl->getTypeLoc().getLoc(), "@escaping ");
475+
} else if (isAutoClosure)
476+
// TODO: add in a fixit for autoclosure
468477
TC.diagnose(DRE->getDecl()->getLoc(), diag::noescape_autoclosure,
469478
DRE->getDecl()->getName());
470479
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1628,10 +1628,6 @@ void TypeChecker::checkNoEscapeAttr(ParamDecl *PD, NoEscapeAttr *attr) {
16281628
return;
16291629
}
16301630

1631-
// Just stop if we've already applied this attribute.
1632-
if (FTy->isNoEscape())
1633-
return;
1634-
16351631
// This range can be implicit e.g. if we're in the middle of diagnosing
16361632
// @autoclosure.
16371633
auto attrRemovalRange = attr->getRangeWithAt();
@@ -1645,6 +1641,10 @@ void TypeChecker::checkNoEscapeAttr(ParamDecl *PD, NoEscapeAttr *attr) {
16451641
.fixItRemove(attrRemovalRange)
16461642
.fixItInsert(PD->getTypeLoc().getSourceRange().Start, "@noescape ");
16471643

1644+
// Stop if we've already applied this attribute.
1645+
if (FTy->isNoEscape())
1646+
return;
1647+
16481648
// Change the type to include the noescape bit.
16491649
PD->overwriteType(FunctionType::get(FTy->getInput(), FTy->getResult(),
16501650
FTy->getExtInfo().withNoEscape(true)));

lib/Sema/TypeCheckCaptures.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,22 @@ class FindCapturedVars : public ASTWalker {
154154
// Otherwise, diagnose this as an invalid capture.
155155
bool isDecl = AFR.getAbstractFunctionDecl() != nullptr;
156156

157-
TC.diagnose(Loc, isDecl ? diag::decl_closure_noescape_use :
158-
diag::closure_noescape_use, VD->getName());
159-
160-
if (VD->getType()->castTo<AnyFunctionType>()->isAutoClosure())
161-
TC.diagnose(VD->getLoc(), diag::noescape_autoclosure,
162-
VD->getName());
157+
TC.diagnose(Loc, isDecl ? diag::decl_closure_noescape_use
158+
: diag::closure_noescape_use,
159+
VD->getName());
160+
161+
// If we're a parameter, emit a helpful fixit to add @escaping
162+
auto paramDecl = dyn_cast<ParamDecl>(VD);
163+
bool isAutoClosure =
164+
VD->getType()->castTo<AnyFunctionType>()->isAutoClosure();
165+
if (paramDecl && !isAutoClosure) {
166+
TC.diagnose(paramDecl->getStartLoc(), diag::noescape_parameter,
167+
paramDecl->getName())
168+
.fixItInsert(paramDecl->getTypeLoc().getLoc(), "@escaping ");
169+
} else if (isAutoClosure) {
170+
// TODO: add in a fixit for autoclosure
171+
TC.diagnose(VD->getLoc(), diag::noescape_autoclosure, VD->getName());
172+
}
163173
}
164174
}
165175

lib/Sema/TypeCheckType.cpp

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,13 +1579,25 @@ Type TypeChecker::resolveType(TypeRepr *TyR, DeclContext *DC,
15791579
return result;
15801580
}
15811581

1582+
/// Whether the given DC is a noescape-by-default context, i.e. not a property
1583+
/// setter
1584+
static bool isDefaultNoEscapeContext(const DeclContext *DC) {
1585+
auto funcDecl = dyn_cast<FuncDecl>(DC);
1586+
return !funcDecl || !funcDecl->isSetter();
1587+
}
1588+
15821589
Type TypeResolver::resolveType(TypeRepr *repr, TypeResolutionOptions options) {
15831590
assert(repr && "Cannot validate null TypeReprs!");
15841591

15851592
// If we know the type representation is invalid, just return an
15861593
// error type.
15871594
if (repr->isInvalid()) return ErrorType::get(TC.Context);
15881595

1596+
// Remember whether this is a function parameter.
1597+
bool isFunctionParam =
1598+
options.contains(TR_FunctionInput) ||
1599+
options.contains(TR_ImmediateFunctionInput);
1600+
15891601
// Strip the "is function input" bits unless this is a type that knows about
15901602
// them.
15911603
if (!isa<InOutTypeRepr>(repr) && !isa<TupleTypeRepr>(repr) &&
@@ -1611,8 +1623,14 @@ Type TypeResolver::resolveType(TypeRepr *repr, TypeResolutionOptions options) {
16111623
UnsatisfiedDependency);
16121624

16131625
case TypeReprKind::Function:
1614-
if (!(options & TR_SILType))
1615-
return resolveASTFunctionType(cast<FunctionTypeRepr>(repr), options);
1626+
if (!(options & TR_SILType)) {
1627+
// Default non-escaping for closure parameters
1628+
auto info = AnyFunctionType::ExtInfo().withNoEscape(
1629+
isFunctionParam &&
1630+
isDefaultNoEscapeContext(DC));
1631+
return resolveASTFunctionType(cast<FunctionTypeRepr>(repr), options,
1632+
info);
1633+
}
16161634
return resolveSILFunctionType(cast<FunctionTypeRepr>(repr), options);
16171635

16181636
case TypeReprKind::Array:
@@ -1870,11 +1888,22 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
18701888
.fixItReplace(resultRange, "Never");
18711889
}
18721890

1891+
bool defaultNoEscape = false;
1892+
// TODO: Get rid of the need for checking autoclosure, by refactoring
1893+
// special autoclosure knowledge to just as "isEscaping" or similar.
1894+
if (isFunctionParam && !attrs.has(TAK_autoclosure)) {
1895+
// Closure params default to non-escaping
1896+
if (attrs.has(TAK_noescape)) {
1897+
// FIXME: diagnostic to tell user this is redundant and drop it
1898+
} else if (!attrs.has(TAK_escaping)) {
1899+
defaultNoEscape = isDefaultNoEscapeContext(DC);
1900+
}
1901+
}
1902+
18731903
// Resolve the function type directly with these attributes.
18741904
FunctionType::ExtInfo extInfo(rep,
18751905
attrs.has(TAK_autoclosure),
1876-
attrs.has(TAK_noescape),
1877-
attrs.has(TAK_escaping),
1906+
defaultNoEscape | attrs.has(TAK_noescape),
18781907
fnRepr->throws());
18791908

18801909
ty = resolveASTFunctionType(fnRepr, options, extInfo);

lib/Serialization/Deserialization.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3493,13 +3493,12 @@ Type ModuleFile::getType(TypeID TID) {
34933493
TypeID inputID;
34943494
TypeID resultID;
34953495
uint8_t rawRepresentation;
3496-
bool autoClosure, noescape, explicitlyEscaping, throws;
3496+
bool autoClosure, noescape, throws;
34973497

34983498
decls_block::FunctionTypeLayout::readRecord(scratch, inputID, resultID,
34993499
rawRepresentation,
35003500
autoClosure,
35013501
noescape,
3502-
explicitlyEscaping,
35033502
throws);
35043503
auto representation = getActualFunctionTypeRepresentation(rawRepresentation);
35053504
if (!representation.hasValue()) {
@@ -3509,7 +3508,7 @@ Type ModuleFile::getType(TypeID TID) {
35093508

35103509
auto Info = FunctionType::ExtInfo(*representation,
35113510
autoClosure, noescape,
3512-
explicitlyEscaping, throws);
3511+
throws);
35133512

35143513
typeOrOffset = FunctionType::get(getType(inputID), getType(resultID),
35153514
Info);

0 commit comments

Comments
 (0)