Skip to content

Commit b3c6c5e

Browse files
authored
Merge pull request #33094 from DougGregor/se-0286-trailing-closure-sourcecompat-5.3
[5.3] [SE-0286] Trailing closure forward scan
2 parents ab06af8 + 1af4c6d commit b3c6c5e

33 files changed

+1226
-384
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,12 @@ ERROR(extra_trailing_closure_in_call,none,
11741174
ERROR(trailing_closure_bad_param,none,
11751175
"trailing closure passed to parameter of type %0 that does not "
11761176
"accept a closure", (Type))
1177+
WARNING(unlabeled_trailing_closure_deprecated,none,
1178+
"backward matching of the unlabeled trailing closure is deprecated; label the argument with %0 to suppress this warning",
1179+
(Identifier))
1180+
NOTE(decl_multiple_defaulted_closure_parameters,none,
1181+
"%0 contains defaulted closure parameters %1 and %2",
1182+
(DeclName, Identifier, Identifier))
11771183
NOTE(candidate_with_extraneous_args,none,
11781184
"candidate %0 requires %1 argument%s1, "
11791185
"but %2 %select{were|was}3 %select{provided|used in closure body}4",

include/swift/AST/Types.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3484,6 +3484,7 @@ END_CAN_TYPE_WRAPPER(FunctionType, AnyFunctionType)
34843484
/// has a default argument.
34853485
struct ParameterListInfo {
34863486
SmallBitVector defaultArguments;
3487+
SmallBitVector acceptsUnlabeledTrailingClosures;
34873488

34883489
public:
34893490
ParameterListInfo() { }
@@ -3494,6 +3495,10 @@ struct ParameterListInfo {
34943495
/// Whether the parameter at the given index has a default argument.
34953496
bool hasDefaultArgument(unsigned paramIdx) const;
34963497

3498+
/// Whether the parameter accepts an unlabeled trailing closure argument
3499+
/// according to the "forward-scan" rule.
3500+
bool acceptsUnlabeledTrailingClosureArgument(unsigned paramIdx) const;
3501+
34973502
/// Retrieve the number of non-defaulted parameters.
34983503
unsigned numNonDefaultedParameters() const {
34993504
return defaultArguments.count();

include/swift/Basic/LangOptions.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,14 @@ namespace swift {
250250
/// Build the ASTScope tree lazily
251251
bool LazyASTScopes = true;
252252

253+
/// Whether to enable the "fuzzy" forward-scanning behavior for trailing
254+
/// closure matching, which skips over defaulted closure parameters
255+
/// to match later (non-defaulted) closure parameters
256+
///
257+
/// This is a backward-compatibility hack for unlabeled trailing closures,
258+
/// to be disabled in Swift 6+.
259+
bool EnableFuzzyForwardScanTrailingClosureMatching = true;
260+
253261
/// Use Clang function types for computing canonical types.
254262
/// If this option is false, the clang function types will still be computed
255263
/// but will not be used for checking type equality.

include/swift/Option/Options.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,16 @@ def enable_experimental_concise_pound_file : Flag<["-"],
519519
Flags<[FrontendOption, ModuleInterfaceOption]>,
520520
HelpText<"Enable experimental concise '#file' identifier">;
521521

522+
def disable_fuzzy_forward_scan_trailing_closure_matching : Flag<["-"],
523+
"disable-fuzzy-forward-scan-trailing-closure-matching">,
524+
Flags<[FrontendOption]>,
525+
HelpText<"Disable fuzzy forward-scan trailing closure matching">;
526+
527+
def enable_fuzzy_forward_scan_trailing_closure_matching : Flag<["-"],
528+
"enable-fuzzy-forward-scan-trailing-closure-matching">,
529+
Flags<[FrontendOption]>,
530+
HelpText<"Enable fuzzy forward-scan trailing closure matching">;
531+
522532
// Diagnostic control options
523533
def suppress_warnings : Flag<["-"], "suppress-warnings">,
524534
Flags<[FrontendOption]>,

lib/AST/Expr.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,15 +1271,29 @@ SourceRange TupleExpr::getSourceRange() const {
12711271
return { SourceLoc(), SourceLoc() };
12721272
} else {
12731273
// Scan backwards for a valid source loc.
1274+
bool hasSingleTrailingClosure = hasTrailingClosure();
12741275
for (Expr *expr : llvm::reverse(getElements())) {
12751276
// Default arguments are located at the start of their parent tuple, so
12761277
// skip over them.
12771278
if (isa<DefaultArgumentExpr>(expr))
12781279
continue;
1279-
end = expr->getEndLoc();
1280-
if (end.isValid()) {
1281-
break;
1280+
1281+
SourceLoc newEnd = expr->getEndLoc();
1282+
if (newEnd.isInvalid())
1283+
continue;
1284+
1285+
// There is a quirk with the backward scan logic for trailing
1286+
// closures that can cause arguments to be flipped. If there is a
1287+
// single trailing closure, only stop when the "end" point we hit comes
1288+
// after the close parenthesis (if there is one).
1289+
if (end.isInvalid() ||
1290+
end.getOpaquePointerValue() < newEnd.getOpaquePointerValue()) {
1291+
end = newEnd;
12821292
}
1293+
1294+
if (!hasSingleTrailingClosure || RParenLoc.isInvalid() ||
1295+
RParenLoc.getOpaquePointerValue() < end.getOpaquePointerValue())
1296+
break;
12831297
}
12841298
}
12851299
} else {

lib/AST/Type.cpp

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,32 @@ Type TypeBase::replaceCovariantResultType(Type newResultType,
822822
return FunctionType::get(inputType, resultType, fnType->getExtInfo());
823823
}
824824

825+
/// Whether this parameter accepts an unlabeled trailing closure argument
826+
/// using the more-restrictive forward-scan rule.
827+
static bool allowsUnlabeledTrailingClosureParameter(const ParamDecl *param) {
828+
// inout parameters never allow an unlabeled trailing closure.
829+
if (param->isInOut())
830+
return false;
831+
832+
Type paramType = param->isVariadic() ? param->getVarargBaseTy()
833+
: param->getInterfaceType();
834+
paramType = paramType->getRValueType()->lookThroughAllOptionalTypes();
835+
836+
// For autoclosure parameters, look through the autoclosure result type
837+
// to get the actual argument type.
838+
if (param->isAutoClosure()) {
839+
auto fnType = paramType->getAs<AnyFunctionType>();
840+
if (!fnType)
841+
return false;
842+
843+
paramType = fnType->getResult()->lookThroughAllOptionalTypes();
844+
}
845+
846+
// After lookup through all optional types, this parameter allows an
847+
// unlabeled trailing closure if it is (structurally) a function type.
848+
return paramType->is<AnyFunctionType>();
849+
}
850+
825851
ParameterListInfo::ParameterListInfo(
826852
ArrayRef<AnyFunctionType::Param> params,
827853
const ValueDecl *paramOwner,
@@ -831,7 +857,7 @@ ParameterListInfo::ParameterListInfo(
831857
// No parameter owner means no parameter list means no default arguments
832858
// - hand back the zeroed bitvector.
833859
//
834-
// FIXME: We ought to not request default argument info in this case.
860+
// FIXME: We ought to not request paramer list info in this case.
835861
if (!paramOwner)
836862
return;
837863

@@ -865,12 +891,21 @@ ParameterListInfo::ParameterListInfo(
865891
if (params.size() != paramList->size())
866892
return;
867893

868-
// Note which parameters have default arguments and/or function builders.
894+
// Now we have enough information to determine which parameters accept
895+
// unlabled trailing closures.
896+
acceptsUnlabeledTrailingClosures.resize(params.size());
897+
898+
// Note which parameters have default arguments and/or accept unlabeled
899+
// trailing closure arguments with the forward-scan rule.
869900
for (auto i : range(0, params.size())) {
870901
auto param = paramList->get(i);
871902
if (param->isDefaultArgument()) {
872903
defaultArguments.set(i);
873904
}
905+
906+
if (allowsUnlabeledTrailingClosureParameter(param)) {
907+
acceptsUnlabeledTrailingClosures.set(i);
908+
}
874909
}
875910
}
876911

@@ -879,6 +914,12 @@ bool ParameterListInfo::hasDefaultArgument(unsigned paramIdx) const {
879914
: false;
880915
}
881916

917+
bool ParameterListInfo::acceptsUnlabeledTrailingClosureArgument(
918+
unsigned paramIdx) const {
919+
return paramIdx >= acceptsUnlabeledTrailingClosures.size() ||
920+
acceptsUnlabeledTrailingClosures[paramIdx];
921+
}
922+
882923
/// Turn a param list into a symbolic and printable representation that does not
883924
/// include the types, something like (_:, b:, c:)
884925
std::string swift::getParamListAsString(ArrayRef<AnyFunctionType::Param> params) {

lib/Driver/ToolChains.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
252252
inputArgs.AddLastArg(arguments, options::OPT_disable_parser_lookup);
253253
inputArgs.AddLastArg(arguments,
254254
options::OPT_enable_experimental_concise_pound_file);
255+
inputArgs.AddLastArg(
256+
arguments,
257+
options::OPT_enable_fuzzy_forward_scan_trailing_closure_matching,
258+
options::OPT_disable_fuzzy_forward_scan_trailing_closure_matching);
255259
inputArgs.AddLastArg(arguments,
256260
options::OPT_verify_incremental_dependencies);
257261

lib/Frontend/CompilerInvocation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,10 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
559559

560560
Opts.EnableConcisePoundFile =
561561
Args.hasArg(OPT_enable_experimental_concise_pound_file);
562+
Opts.EnableFuzzyForwardScanTrailingClosureMatching =
563+
Args.hasFlag(OPT_enable_fuzzy_forward_scan_trailing_closure_matching,
564+
OPT_disable_fuzzy_forward_scan_trailing_closure_matching,
565+
true);
562566

563567
Opts.EnableCrossImportOverlays =
564568
Args.hasFlag(OPT_enable_cross_import_overlays,

0 commit comments

Comments
 (0)