Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,11 @@ SIMPLE_DECL_ATTR(noDerivative, NoDerivative,
ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove,
100)

DECL_ATTR(_trailingClosureMatching, TrailingClosureMatching,
OnAbstractFunction | OnSubscript | OnEnumElement | UserInaccessible |
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
101)

#undef TYPE_ATTR
#undef DECL_ATTR_ALIAS
#undef CONTEXTUAL_DECL_ATTR_ALIAS
Expand Down
21 changes: 21 additions & 0 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class GenericFunctionType;
class LazyConformanceLoader;
class LazyMemberLoader;
class PatternBindingInitializer;
enum class TrailingClosureMatching: uint8_t;
class TrailingWhereClause;
class TypeExpr;

Expand Down Expand Up @@ -2046,6 +2047,26 @@ class TransposeAttr final
}
};

/// Attribute that controls the matching of trailing closures.
class TrailingClosureMatchingAttr final : public DeclAttribute {
TrailingClosureMatching matchingRule;

public:
TrailingClosureMatchingAttr(SourceLoc atLoc, SourceRange range,
TrailingClosureMatching matchingRule,
bool implicit)
: DeclAttribute(DAK_TrailingClosureMatching, atLoc, range, implicit),
matchingRule(matchingRule) {}

TrailingClosureMatching getMatchingRule() const {
return matchingRule;
}

static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DAK_TrailingClosureMatching;
}
};

/// Attributes that may be applied to declarations.
class DeclAttributes {
/// Linked list of declaration attributes.
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -1372,6 +1372,10 @@ ERROR(attr_only_at_non_local_scope, none,
ERROR(projection_value_property_not_identifier,none,
"@_projectedValueProperty name must be an identifier", ())

ERROR(trailing_closure_matching_missing_dot_identifier,none,
"@_trailingClosureMatching parameter must be '.forward' or '.backward'",
())

// Access control
ERROR(attr_access_expected_set,none,
"expected 'set' as subject of '%0' modifier", (StringRef))
Expand Down
23 changes: 23 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -3492,20 +3492,43 @@ BEGIN_CAN_TYPE_WRAPPER(FunctionType, AnyFunctionType)
}
END_CAN_TYPE_WRAPPER(FunctionType, AnyFunctionType)

/// Describes the matching rules for trailing closures.
enum class TrailingClosureMatching: uint8_t {
/// Match trailing closures from the end of the parameter list, which is
/// the default rule for Swift < 6.
Backward,
/// Match trailing closures by scanning "forward" from the previously-
/// matched parameter and skipping over parameters that aren't structurally
/// function types.
Forward,
};

/// Provides information about the parameter list of a given declaration, including whether each parameter
/// has a default argument.
struct ParameterListInfo {
SmallBitVector defaultArguments;
SmallBitVector acceptsUnlabeledTrailingClosures;
Optional<TrailingClosureMatching> trailingClosureMatching;

public:
ParameterListInfo() { }

ParameterListInfo(ArrayRef<AnyFunctionType::Param> params,
const ValueDecl *paramOwner, bool skipCurriedSelf);

/// Retrieve the trailing closure matching rule, if one was explicitly
/// specified.
Optional<TrailingClosureMatching> getTrailingClosureMatching() const {
return trailingClosureMatching;
}

/// Whether the parameter at the given index has a default argument.
bool hasDefaultArgument(unsigned paramIdx) const;

/// Whether the parameter accepts an unlabeled trailing closure argument
/// according to the "forward-scan" rule.
bool acceptsUnlabeledTrailingClosureArgument(unsigned paramIdx) const;

/// Retrieve the number of non-defaulted parameters.
unsigned numNonDefaultedParameters() const {
return defaultArguments.count();
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ namespace swift {
/// behavior. This is a staging flag, and will be removed in the future.
bool EnableNewOperatorLookup = false;

/// Whether to default to the "forward" scan for trailing closure matching.
bool EnableForwardTrailingClosureMatching = false;

/// Use Clang function types for computing canonical types.
/// If this option is false, the clang function types will still be computed
/// but will not be used for checking type equality.
Expand Down
5 changes: 5 additions & 0 deletions include/swift/Option/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,11 @@ def enable_experimental_concise_pound_file : Flag<["-"],
Flags<[FrontendOption]>,
HelpText<"Enable experimental concise '#file' identifier">;

def enable_experimental_trailing_closure_matching : Flag<["-"],
"enable-experimental-trailing-closure-matching">,
Flags<[FrontendOption]>,
HelpText<"Enable the experimental 'forward' matching rule for trailing closures">;

def enable_direct_intramodule_dependencies : Flag<["-"],
"enable-direct-intramodule-dependencies">,
Flags<[FrontendOption, HelpHidden]>,
Expand Down
18 changes: 18 additions & 0 deletions lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,22 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
break;
}

case DAK_TrailingClosureMatching: {
Printer.printAttrName("@_trailingClosureMatching");
Printer << '(';
switch (cast<TrailingClosureMatchingAttr>(this)->getMatchingRule()) {
case TrailingClosureMatching::Backward:
Printer << ".backward";
break;

case TrailingClosureMatching::Forward:
Printer << ".forward";
break;
}
Printer << ')';
break;
}

case DAK_Count:
llvm_unreachable("exceed declaration attribute kinds");

Expand Down Expand Up @@ -1217,6 +1233,8 @@ StringRef DeclAttribute::getAttrName() const {
return "derivative";
case DAK_Transpose:
return "transpose";
case DAK_TrailingClosureMatching:
return "_trailingClosureMatching";
}
llvm_unreachable("bad DeclAttrKind");
}
Expand Down
51 changes: 49 additions & 2 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -821,16 +821,46 @@ Type TypeBase::replaceCovariantResultType(Type newResultType,
return FunctionType::get(inputType, resultType, fnType->getExtInfo());
}

/// Whether this parameter accepts an unlabeled trailing closure argument
/// using the more-restrictive forward-scan rule.
static bool allowsUnlabeledTrailingClosureParameter(const ParamDecl *param) {
// Variadic parameters never allow an unlabeled trailing closure.
if (param->isVariadic())
return false;

// inout parameters never allow an unlabeled trailing closure.
if (param->isInOut())
return false;

Type paramType =
param->getInterfaceType()->getRValueType()->lookThroughAllOptionalTypes();

// For autoclosure parameters, look through the autoclosure result type
// to get the actual argument type.
if (param->isAutoClosure()) {
auto fnType = paramType->getAs<AnyFunctionType>();
if (!fnType)
return false;

paramType = fnType->getResult()->lookThroughAllOptionalTypes();
}

// After lookup through all optional types, this parameter allows an
// unlabeled trailing closure if it is (structurally) a function type.
return paramType->is<AnyFunctionType>();
}

ParameterListInfo::ParameterListInfo(
ArrayRef<AnyFunctionType::Param> params,
const ValueDecl *paramOwner,
bool skipCurriedSelf) {
defaultArguments.resize(params.size());
acceptsUnlabeledTrailingClosures.resize(params.size());

// No parameter owner means no parameter list means no default arguments
// - hand back the zeroed bitvector.
//
// FIXME: We ought to not request default argument info in this case.
// FIXME: We ought to not request paramer list info in this case.
if (!paramOwner)
return;

Expand All @@ -848,6 +878,12 @@ ParameterListInfo::ParameterListInfo(
paramList = enumElement->getParameterList();
}

// Retrieve the explicitly-specified trailing closure matching rule.
if (auto attr =
paramOwner->getAttrs().getAttribute<TrailingClosureMatchingAttr>()) {
trailingClosureMatching = attr->getMatchingRule();
}

// No parameter list means no default arguments - hand back the zeroed
// bitvector.
if (!paramList) {
Expand All @@ -864,12 +900,17 @@ ParameterListInfo::ParameterListInfo(
if (params.size() != paramList->size())
return;

// Note which parameters have default arguments and/or function builders.
// Note which parameters have default arguments and/or accept unlabeled
// trailing closure arguments with the forward-scan rule.
for (auto i : range(0, params.size())) {
auto param = paramList->get(i);
if (param->isDefaultArgument()) {
defaultArguments.set(i);
}

if (allowsUnlabeledTrailingClosureParameter(param)) {
acceptsUnlabeledTrailingClosures.set(i);
}
}
}

Expand All @@ -878,6 +919,12 @@ bool ParameterListInfo::hasDefaultArgument(unsigned paramIdx) const {
: false;
}

bool ParameterListInfo::acceptsUnlabeledTrailingClosureArgument(
unsigned paramIdx) const {
return paramIdx < acceptsUnlabeledTrailingClosures.size() &&
acceptsUnlabeledTrailingClosures[paramIdx];
}

/// Turn a param list into a symbolic and printable representation that does not
/// include the types, something like (_:, b:, c:)
std::string swift::getParamListAsString(ArrayRef<AnyFunctionType::Param> params) {
Expand Down
3 changes: 3 additions & 0 deletions lib/Driver/ToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
inputArgs.AddLastArg(arguments, options::OPT_disable_parser_lookup);
inputArgs.AddLastArg(arguments,
options::OPT_enable_experimental_concise_pound_file);
inputArgs.AddLastArg(
arguments,
options::OPT_enable_experimental_trailing_closure_matching);
inputArgs.AddLastArg(arguments,
options::OPT_verify_incremental_dependencies);
inputArgs.AddLastArg(arguments,
Expand Down
2 changes: 2 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,

Opts.EnableConcisePoundFile =
Args.hasArg(OPT_enable_experimental_concise_pound_file);
Opts.EnableForwardTrailingClosureMatching =
Args.hasArg(OPT_enable_experimental_trailing_closure_matching);

Opts.EnableCrossImportOverlays =
Args.hasFlag(OPT_enable_cross_import_overlays,
Expand Down
43 changes: 43 additions & 0 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2301,6 +2301,49 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
name, AtLoc, range, /*implicit*/ false));
break;
}

case DAK_TrailingClosureMatching: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}

if (!consumeIf(tok::period_prefix)) {
diagnose(Loc, diag::trailing_closure_matching_missing_dot_identifier);
return false;
}

if (Tok.isNot(tok::identifier)) {
diagnose(Loc, diag::trailing_closure_matching_missing_dot_identifier);
return false;
}

Identifier name;
consumeIdentifier(&name, /*allowDollarIdentifier=*/true);

TrailingClosureMatching matchingRule;
if (name.str() == "forward")
matchingRule = TrailingClosureMatching::Forward;
else if (name.str() == "backward")
matchingRule = TrailingClosureMatching::Backward;
else {
diagnose(Loc, diag::trailing_closure_matching_missing_dot_identifier);
return false;
}

auto range = SourceRange(Loc, Tok.getRange().getStart());

if (!consumeIf(tok::r_paren)) {
diagnose(Loc, diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}

Attributes.add(new (Context) TrailingClosureMatchingAttr(
AtLoc, range, matchingRule, /*implicit=*/false));
break;
}
}

if (DuplicateAttribute) {
Expand Down
Loading