Skip to content

Commit 73f07a6

Browse files
authored
Merge pull request #33092 from DougGregor/forward-trailing-closure-matching-sourcecompat-backward-bias
[SE-0286] Forward matching of trailing closure arguments
2 parents 6187697 + 2979d4a commit 73f07a6

33 files changed

+1223
-390
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,12 @@ ERROR(extra_trailing_closure_in_call,none,
11891189
ERROR(trailing_closure_bad_param,none,
11901190
"trailing closure passed to parameter of type %0 that does not "
11911191
"accept a closure", (Type))
1192+
WARNING(unlabeled_trailing_closure_deprecated,none,
1193+
"backward matching of the unlabeled trailing closure is deprecated; label the argument with %0 to suppress this warning",
1194+
(Identifier))
1195+
NOTE(decl_multiple_defaulted_closure_parameters,none,
1196+
"%0 contains defaulted closure parameters %1 and %2",
1197+
(DeclName, Identifier, Identifier))
11921198
NOTE(candidate_with_extraneous_args,none,
11931199
"candidate %0 requires %1 argument%s1, "
11941200
"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
@@ -3500,6 +3500,7 @@ END_CAN_TYPE_WRAPPER(FunctionType, AnyFunctionType)
35003500
/// has a default argument.
35013501
struct ParameterListInfo {
35023502
SmallBitVector defaultArguments;
3503+
SmallBitVector acceptsUnlabeledTrailingClosures;
35033504

35043505
public:
35053506
ParameterListInfo() { }
@@ -3510,6 +3511,10 @@ struct ParameterListInfo {
35103511
/// Whether the parameter at the given index has a default argument.
35113512
bool hasDefaultArgument(unsigned paramIdx) const;
35123513

3514+
/// Whether the parameter accepts an unlabeled trailing closure argument
3515+
/// according to the "forward-scan" rule.
3516+
bool acceptsUnlabeledTrailingClosureArgument(unsigned paramIdx) const;
3517+
35133518
/// Retrieve the number of non-defaulted parameters.
35143519
unsigned numNonDefaultedParameters() const {
35153520
return defaultArguments.count();

include/swift/Basic/LangOptions.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,14 @@ namespace swift {
271271
/// behavior. This is a staging flag, and will be removed in the future.
272272
bool EnableNewOperatorLookup = false;
273273

274+
/// Whether to enable the "fuzzy" forward-scanning behavior for trailing
275+
/// closure matching, which skips over defaulted closure parameters
276+
/// to match later (non-defaulted) closure parameters
277+
///
278+
/// This is a backward-compatibility hack for unlabeled trailing closures,
279+
/// to be disabled in Swift 6+.
280+
bool EnableFuzzyForwardScanTrailingClosureMatching = true;
281+
274282
/// Use Clang function types for computing canonical types.
275283
/// If this option is false, the clang function types will still be computed
276284
/// 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
@@ -546,6 +546,16 @@ def enable_experimental_concise_pound_file : Flag<["-"],
546546
Flags<[FrontendOption, ModuleInterfaceOption]>,
547547
HelpText<"Enable experimental concise '#file' identifier">;
548548

549+
def disable_fuzzy_forward_scan_trailing_closure_matching : Flag<["-"],
550+
"disable-fuzzy-forward-scan-trailing-closure-matching">,
551+
Flags<[FrontendOption]>,
552+
HelpText<"Disable fuzzy forward-scan trailing closure matching">;
553+
554+
def enable_fuzzy_forward_scan_trailing_closure_matching : Flag<["-"],
555+
"enable-fuzzy-forward-scan-trailing-closure-matching">,
556+
Flags<[FrontendOption]>,
557+
HelpText<"Enable fuzzy forward-scan trailing closure matching">;
558+
549559
def enable_direct_intramodule_dependencies : Flag<["-"],
550560
"enable-direct-intramodule-dependencies">,
551561
Flags<[FrontendOption, HelpHidden]>,

lib/AST/Expr.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,15 +1278,29 @@ SourceRange TupleExpr::getSourceRange() const {
12781278
return { SourceLoc(), SourceLoc() };
12791279
} else {
12801280
// Scan backwards for a valid source loc.
1281+
bool hasSingleTrailingClosure = hasTrailingClosure();
12811282
for (Expr *expr : llvm::reverse(getElements())) {
12821283
// Default arguments are located at the start of their parent tuple, so
12831284
// skip over them.
12841285
if (isa<DefaultArgumentExpr>(expr))
12851286
continue;
1286-
end = expr->getEndLoc();
1287-
if (end.isValid()) {
1288-
break;
1287+
1288+
SourceLoc newEnd = expr->getEndLoc();
1289+
if (newEnd.isInvalid())
1290+
continue;
1291+
1292+
// There is a quirk with the backward scan logic for trailing
1293+
// closures that can cause arguments to be flipped. If there is a
1294+
// single trailing closure, only stop when the "end" point we hit comes
1295+
// after the close parenthesis (if there is one).
1296+
if (end.isInvalid() ||
1297+
end.getOpaquePointerValue() < newEnd.getOpaquePointerValue()) {
1298+
end = newEnd;
12891299
}
1300+
1301+
if (!hasSingleTrailingClosure || RParenLoc.isInvalid() ||
1302+
RParenLoc.getOpaquePointerValue() < end.getOpaquePointerValue())
1303+
break;
12901304
}
12911305
}
12921306
} else {

lib/AST/Type.cpp

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

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

@@ -869,12 +895,21 @@ ParameterListInfo::ParameterListInfo(
869895
if (params.size() != paramList->size())
870896
return;
871897

872-
// Note which parameters have default arguments and/or function builders.
898+
// Now we have enough information to determine which parameters accept
899+
// unlabled trailing closures.
900+
acceptsUnlabeledTrailingClosures.resize(params.size());
901+
902+
// Note which parameters have default arguments and/or accept unlabeled
903+
// trailing closure arguments with the forward-scan rule.
873904
for (auto i : range(0, params.size())) {
874905
auto param = paramList->get(i);
875906
if (param->isDefaultArgument()) {
876907
defaultArguments.set(i);
877908
}
909+
910+
if (allowsUnlabeledTrailingClosureParameter(param)) {
911+
acceptsUnlabeledTrailingClosures.set(i);
912+
}
878913
}
879914
}
880915

@@ -883,6 +918,12 @@ bool ParameterListInfo::hasDefaultArgument(unsigned paramIdx) const {
883918
: false;
884919
}
885920

921+
bool ParameterListInfo::acceptsUnlabeledTrailingClosureArgument(
922+
unsigned paramIdx) const {
923+
return paramIdx >= acceptsUnlabeledTrailingClosures.size() ||
924+
acceptsUnlabeledTrailingClosures[paramIdx];
925+
}
926+
886927
/// Turn a param list into a symbolic and printable representation that does not
887928
/// include the types, something like (_:, b:, c:)
888929
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
@@ -264,6 +264,10 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
264264
inputArgs.AddLastArg(arguments, options::OPT_disable_parser_lookup);
265265
inputArgs.AddLastArg(arguments,
266266
options::OPT_enable_experimental_concise_pound_file);
267+
inputArgs.AddLastArg(
268+
arguments,
269+
options::OPT_enable_fuzzy_forward_scan_trailing_closure_matching,
270+
options::OPT_disable_fuzzy_forward_scan_trailing_closure_matching);
267271
inputArgs.AddLastArg(arguments,
268272
options::OPT_verify_incremental_dependencies);
269273
inputArgs.AddLastArg(arguments,

lib/Frontend/CompilerInvocation.cpp

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

573573
Opts.EnableConcisePoundFile =
574574
Args.hasArg(OPT_enable_experimental_concise_pound_file);
575+
Opts.EnableFuzzyForwardScanTrailingClosureMatching =
576+
Args.hasFlag(OPT_enable_fuzzy_forward_scan_trailing_closure_matching,
577+
OPT_disable_fuzzy_forward_scan_trailing_closure_matching,
578+
true);
575579

576580
Opts.EnableCrossImportOverlays =
577581
Args.hasFlag(OPT_enable_cross_import_overlays,

0 commit comments

Comments
 (0)