Skip to content

Commit 9d4116d

Browse files
committed
[CSSimplify] NFC: Extract isSingleTupleParam and missing arguments fix from matchFunctionTypes
1 parent aeaa26d commit 9d4116d

File tree

1 file changed

+110
-92
lines changed

1 file changed

+110
-92
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 110 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,108 @@ static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1,
10631063
llvm_unreachable("Unhandled ConstraintKind in switch.");
10641064
}
10651065

1066+
/// Check whether given parameter list represents a single tuple
1067+
/// or type variable which could be later resolved to tuple.
1068+
/// This is useful for SE-0110 related fixes in `matchFunctionTypes`.
1069+
static bool isSingleTupleParam(ASTContext &ctx,
1070+
ArrayRef<AnyFunctionType::Param> params) {
1071+
if (params.size() != 1)
1072+
return false;
1073+
1074+
const auto &param = params.front();
1075+
if (param.isVariadic() || param.isInOut())
1076+
return false;
1077+
1078+
auto paramType = param.getPlainType();
1079+
// Support following case which was allowed until 5:
1080+
//
1081+
// func bar(_: (Int, Int) -> Void) {}
1082+
// let foo: ((Int, Int)?) -> Void = { _ in }
1083+
//
1084+
// bar(foo) // Ok
1085+
if (!ctx.isSwiftVersionAtLeast(5))
1086+
paramType = paramType->lookThroughAllOptionalTypes();
1087+
1088+
// Parameter should have a label and be either a tuple tuple type,
1089+
// or a type variable which might later be assigned a tuple type,
1090+
// e.g. opened generic parameter.
1091+
return !param.hasLabel() &&
1092+
(paramType->is<TupleType>() || paramType->is<TypeVariableType>());
1093+
}
1094+
1095+
/// Attempt to fix missing arguments by introducing type variables
1096+
/// and inferring their types from parameters.
1097+
static bool fixMissingArguments(ConstraintSystem &cs, Expr *anchor,
1098+
FunctionType *funcType,
1099+
SmallVectorImpl<AnyFunctionType::Param> &args,
1100+
SmallVectorImpl<AnyFunctionType::Param> &params,
1101+
unsigned numMissing,
1102+
ConstraintLocatorBuilder locator) {
1103+
assert(args.size() < params.size());
1104+
1105+
auto &ctx = cs.getASTContext();
1106+
// If there are N parameters but a single closure argument
1107+
// (which might be anonymous), it's most likely used as a
1108+
// tuple e.g. `$0.0`.
1109+
Optional<TypeBase *> argumentTuple;
1110+
if (isa<ClosureExpr>(anchor) && isSingleTupleParam(ctx, args)) {
1111+
auto isParam = [](const Expr *expr) {
1112+
if (auto *DRE = dyn_cast<DeclRefExpr>(expr)) {
1113+
if (auto *decl = DRE->getDecl())
1114+
return isa<ParamDecl>(decl);
1115+
}
1116+
return false;
1117+
};
1118+
1119+
const auto &arg = args.back();
1120+
if (auto *argTy = arg.getPlainType()->getAs<TypeVariableType>()) {
1121+
anchor->forEachChildExpr([&](Expr *expr) -> Expr * {
1122+
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(expr)) {
1123+
if (!isParam(UDE->getBase()))
1124+
return expr;
1125+
1126+
auto name = UDE->getName().getBaseIdentifier();
1127+
unsigned index = 0;
1128+
if (!name.str().getAsInteger(10, index) ||
1129+
llvm::any_of(params, [&](const AnyFunctionType::Param &param) {
1130+
return param.getLabel() == name;
1131+
})) {
1132+
argumentTuple.emplace(argTy);
1133+
args.pop_back();
1134+
return nullptr;
1135+
}
1136+
}
1137+
return expr;
1138+
});
1139+
}
1140+
}
1141+
1142+
for (unsigned i = args.size(), n = params.size(); i != n; ++i) {
1143+
auto *argLoc = cs.getConstraintLocator(
1144+
anchor, LocatorPathElt::getSynthesizedArgument(i));
1145+
args.push_back(params[i].withType(cs.createTypeVariable(argLoc)));
1146+
}
1147+
1148+
ArrayRef<AnyFunctionType::Param> argsRef(args);
1149+
auto *fix =
1150+
AddMissingArguments::create(cs, funcType, argsRef.take_back(numMissing),
1151+
cs.getConstraintLocator(locator));
1152+
1153+
if (cs.recordFix(fix))
1154+
return true;
1155+
1156+
// If the argument was a single "tuple", let's bind newly
1157+
// synthesized arguments to it.
1158+
if (argumentTuple) {
1159+
cs.addConstraint(ConstraintKind::Bind, *argumentTuple,
1160+
FunctionType::composeInput(ctx, args,
1161+
/*canonicalVararg=*/false),
1162+
cs.getConstraintLocator(anchor));
1163+
}
1164+
1165+
return false;
1166+
}
1167+
10661168
ConstraintSystem::TypeMatchResult
10671169
ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
10681170
ConstraintKind kind, TypeMatchOptions flags,
@@ -1137,33 +1239,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
11371239
// Add a very narrow exception to SE-0110 by allowing functions that
11381240
// take multiple arguments to be passed as an argument in places
11391241
// that expect a function that takes a single tuple (of the same
1140-
// arity).
1141-
auto isSingleParam = [&](ArrayRef<AnyFunctionType::Param> params) {
1142-
if (params.size() != 1)
1143-
return false;
1144-
1145-
const auto &param = params.front();
1146-
if (param.isVariadic() || param.isInOut())
1147-
return false;
1148-
1149-
auto paramType = param.getPlainType();
1150-
// Support following case which was allowed until 5:
1151-
// ``swift
1152-
// func bar(_: (Int, Int) -> Void) {}
1153-
// let foo: ((Int, Int)?) -> Void = { _ in }
1154-
//
1155-
// bar(foo) // Ok
1156-
// ```
1157-
if (!getASTContext().isSwiftVersionAtLeast(5))
1158-
paramType = paramType->lookThroughAllOptionalTypes();
1159-
1160-
// Parameter should have a label and be either a tuple tuple type,
1161-
// or a type variable which might later be assigned a tuple type,
1162-
// e.g. opened generic parameter.
1163-
return !param.hasLabel() &&
1164-
(paramType->is<TupleType>() || paramType->is<TypeVariableType>());
1165-
};
1166-
1242+
// arity);
11671243
auto canImplodeParams = [&](ArrayRef<AnyFunctionType::Param> params) {
11681244
if (params.size() == 1)
11691245
return false;
@@ -1194,12 +1270,13 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
11941270
});
11951271

11961272
if (last != path.rend()) {
1273+
auto &ctx = getASTContext();
11971274
if (last->getKind() == ConstraintLocator::ApplyArgToParam) {
1198-
if (isSingleParam(func2Params) &&
1275+
if (isSingleTupleParam(ctx, func2Params) &&
11991276
canImplodeParams(func1Params)) {
12001277
implodeParams(func1Params);
1201-
} else if (!getASTContext().isSwiftVersionAtLeast(5) &&
1202-
isSingleParam(func1Params) &&
1278+
} else if (!ctx.isSwiftVersionAtLeast(5) &&
1279+
isSingleTupleParam(ctx, func1Params) &&
12031280
canImplodeParams(func2Params)) {
12041281
auto *simplified = locator.trySimplifyToExpr();
12051282
// We somehow let tuple unsplatting function conversions
@@ -1236,7 +1313,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
12361313

12371314
if (last != path.rend()) {
12381315
if (last->getKind() == ConstraintLocator::ApplyArgToParam) {
1239-
if (isSingleParam(func1Params) &&
1316+
if (isSingleTupleParam(getASTContext(), func1Params) &&
12401317
func1Params[0].getOldType()->isVoid()) {
12411318
if (func2Params.empty()) {
12421319
func2Params.emplace_back(getASTContext().TheEmptyTupleType);
@@ -1262,68 +1339,9 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
12621339
// If there are missing arguments, let's add them
12631340
// using parameter as a template.
12641341
if (diff < 0) {
1265-
// If there are N parameters but a single closure argument
1266-
// (which might be anonymous), it's most likely used as a
1267-
// tuple e.g. `$0.0`.
1268-
Optional<TypeBase *> argumentTuple;
1269-
if (isa<ClosureExpr>(anchor) && isSingleParam(func1Params)) {
1270-
auto isParam = [](const Expr *expr) {
1271-
if (auto *DRE = dyn_cast<DeclRefExpr>(expr)) {
1272-
if (auto *decl = DRE->getDecl())
1273-
return isa<ParamDecl>(decl);
1274-
}
1275-
return false;
1276-
};
1277-
1278-
const auto &arg = func1Params.back();
1279-
if (auto *argTy = arg.getPlainType()->getAs<TypeVariableType>()) {
1280-
anchor->forEachChildExpr([&](Expr *expr) -> Expr * {
1281-
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(expr)) {
1282-
if (!isParam(UDE->getBase()))
1283-
return expr;
1284-
1285-
auto name = UDE->getName().getBaseIdentifier();
1286-
unsigned index = 0;
1287-
if (!name.str().getAsInteger(10, index) ||
1288-
llvm::any_of(func2Params,
1289-
[&](const AnyFunctionType::Param &param) {
1290-
return param.getLabel() == name;
1291-
})) {
1292-
argumentTuple.emplace(argTy);
1293-
func1Params.pop_back();
1294-
return nullptr;
1295-
}
1296-
}
1297-
return expr;
1298-
});
1299-
}
1300-
}
1301-
1302-
for (unsigned i = func1Params.size(),
1303-
n = func2Params.size(); i != n; ++i) {
1304-
auto *argLoc = getConstraintLocator(
1305-
anchor, LocatorPathElt::getSynthesizedArgument(i));
1306-
1307-
auto arg = func2Params[i].withType(createTypeVariable(argLoc));
1308-
func1Params.push_back(arg);
1309-
}
1310-
1311-
ArrayRef<AnyFunctionType::Param> argsRef(func1Params);
1312-
auto *fix = AddMissingArguments::create(*this, func2,
1313-
argsRef.take_back(abs(diff)),
1314-
getConstraintLocator(locator));
1315-
1316-
if (recordFix(fix))
1342+
if (fixMissingArguments(*this, anchor, func2, func1Params, func2Params,
1343+
abs(diff), locator))
13171344
return getTypeMatchFailure(argumentLocator);
1318-
1319-
// If the argument was a single "tuple", let's bind newly
1320-
// synthesized arguments to it.
1321-
if (argumentTuple) {
1322-
addConstraint(ConstraintKind::Bind, *argumentTuple,
1323-
FunctionType::composeInput(getASTContext(), func1Params,
1324-
/*canonicalVararg=*/false),
1325-
getConstraintLocator(anchor));
1326-
}
13271345
} else {
13281346
// TODO(diagnostics): Add handling of extraneous arguments.
13291347
return getTypeMatchFailure(argumentLocator);

0 commit comments

Comments
 (0)