Skip to content

Commit 9ff887b

Browse files
authored
Merge pull request #26165 from xedin/move-single-tuple-param-splat
[Diagnostics] Add a diagnostic for single parameter tuple splat
2 parents bc46936 + 3697bd5 commit 9ff887b

File tree

10 files changed

+235
-163
lines changed

10 files changed

+235
-163
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3392,9 +3392,12 @@ ERROR(closure_tuple_parameter_destructuring_implicit,none,
33923392
ERROR(nested_tuple_parameter_destructuring,none,
33933393
"nested tuple parameter %0 of function %1 "
33943394
"does not support destructuring", (Type, Type))
3395-
ERROR(single_tuple_parameter_mismatch,none,
3396-
"%0 %select{|%1 }3expects a single parameter of type %2",
3397-
(DescriptiveDeclKind, Identifier, Type, bool))
3395+
ERROR(single_tuple_parameter_mismatch_special,none,
3396+
"%0 expects a single parameter of type %1%2",
3397+
(DescriptiveDeclKind, Type, StringRef))
3398+
ERROR(single_tuple_parameter_mismatch_normal,none,
3399+
"%0 %1 expects a single parameter of type %2%3",
3400+
(DescriptiveDeclKind, DeclBaseName, Type, StringRef))
33983401
ERROR(unknown_single_tuple_parameter_mismatch,none,
33993402
"single parameter of type %0 is expected in call", (Type))
34003403

lib/Sema/CSDiag.cpp

Lines changed: 0 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -3044,116 +3044,6 @@ bool FailureDiagnosis::diagnoseImplicitSelfErrors(
30443044
return false;
30453045
}
30463046

3047-
static bool diagnoseTupleParameterMismatch(CalleeCandidateInfo &CCI,
3048-
ArrayRef<AnyFunctionType::Param> params,
3049-
ArrayRef<AnyFunctionType::Param> args,
3050-
Expr *fnExpr, Expr *argExpr,
3051-
bool isTopLevel = true) {
3052-
// Try to diagnose function call tuple parameter splat only if
3053-
// there is no trailing or argument closure, because
3054-
// FailureDiagnosis::visitClosureExpr will produce better
3055-
// diagnostic and fix-it for trailing closure case.
3056-
if (isTopLevel) {
3057-
if (CCI.hasTrailingClosure)
3058-
return false;
3059-
3060-
if (auto *parenExpr = dyn_cast<ParenExpr>(argExpr)) {
3061-
if (isa<ClosureExpr>(parenExpr->getSubExpr()))
3062-
return false;
3063-
}
3064-
}
3065-
3066-
if (params.size() == 1 && args.size() == 1) {
3067-
auto paramType = params.front().getOldType();
3068-
auto argType = args.front().getOldType();
3069-
3070-
if (auto *paramFnType = paramType->getAs<AnyFunctionType>()) {
3071-
// Only if both of the parameter and argument types are functions
3072-
// let's recur into diagnosing their arguments.
3073-
if (auto *argFnType = argType->getAs<AnyFunctionType>())
3074-
return diagnoseTupleParameterMismatch(CCI, paramFnType->getParams(),
3075-
argFnType->getParams(), fnExpr,
3076-
argExpr, /* isTopLevel */ false);
3077-
return false;
3078-
}
3079-
}
3080-
3081-
if (params.size() != 1 || args.empty())
3082-
return false;
3083-
3084-
auto paramType = params.front().getOldType();
3085-
3086-
if (args.size() == 1) {
3087-
auto argType = args.front().getOldType();
3088-
if (auto *paramFnType = paramType->getAs<AnyFunctionType>()) {
3089-
// Only if both of the parameter and argument types are functions
3090-
// let's recur into diagnosing their arguments.
3091-
if (auto *argFnType = argType->getAs<AnyFunctionType>())
3092-
return diagnoseTupleParameterMismatch(CCI, paramFnType->getParams(),
3093-
argFnType->getParams(), fnExpr,
3094-
argExpr, /* isTopLevel */ false);
3095-
}
3096-
3097-
return false;
3098-
}
3099-
3100-
// Let's see if inferred argument is actually a tuple inside of Paren.
3101-
auto *paramTupleTy = paramType->getAs<TupleType>();
3102-
if (!paramTupleTy)
3103-
return false;
3104-
3105-
if (paramTupleTy->getNumElements() != args.size())
3106-
return false;
3107-
3108-
// Looks like the number of tuple elements matches number
3109-
// of function arguments, which means we can we can emit an
3110-
// error about an attempt to make use of tuple splat or tuple
3111-
// destructuring, unfortunately we can't provide a fix-it for
3112-
// this case.
3113-
auto &TC = CCI.CS.TC;
3114-
if (isTopLevel) {
3115-
if (auto *decl = CCI[0].getDecl()) {
3116-
Identifier name;
3117-
auto kind = decl->getDescriptiveKind();
3118-
// Constructors/descructors and subscripts don't really have names.
3119-
if (!(isa<ConstructorDecl>(decl) || isa<DestructorDecl>(decl) ||
3120-
isa<SubscriptDecl>(decl))) {
3121-
name = decl->getBaseName().getIdentifier();
3122-
}
3123-
3124-
TC.diagnose(argExpr->getLoc(), diag::single_tuple_parameter_mismatch,
3125-
kind, name, paramTupleTy, !name.empty())
3126-
.highlight(argExpr->getSourceRange())
3127-
.fixItInsertAfter(argExpr->getStartLoc(), "(")
3128-
.fixItInsert(argExpr->getEndLoc(), ")");
3129-
} else {
3130-
TC.diagnose(argExpr->getLoc(),
3131-
diag::unknown_single_tuple_parameter_mismatch, paramTupleTy)
3132-
.highlight(argExpr->getSourceRange())
3133-
.fixItInsertAfter(argExpr->getStartLoc(), "(")
3134-
.fixItInsert(argExpr->getEndLoc(), ")");
3135-
}
3136-
} else {
3137-
TC.diagnose(argExpr->getLoc(),
3138-
diag::nested_tuple_parameter_destructuring, paramTupleTy,
3139-
CCI.CS.getType(fnExpr));
3140-
}
3141-
3142-
return true;
3143-
}
3144-
3145-
static bool diagnoseTupleParameterMismatch(CalleeCandidateInfo &CCI,
3146-
ArrayRef<FunctionType::Param> params,
3147-
Type argType, Expr *fnExpr,
3148-
Expr *argExpr,
3149-
bool isTopLevel = true) {
3150-
llvm::SmallVector<AnyFunctionType::Param, 4> args;
3151-
FunctionType::decomposeInput(argType, args);
3152-
3153-
return diagnoseTupleParameterMismatch(CCI, params, args, fnExpr, argExpr,
3154-
isTopLevel);
3155-
}
3156-
31573047
class ArgumentMatcher : public MatchCallArgumentListener {
31583048
TypeChecker &TC;
31593049
Expr *FnExpr;
@@ -3469,10 +3359,6 @@ diagnoseSingleCandidateFailures(CalleeCandidateInfo &CCI, Expr *fnExpr,
34693359
}
34703360
}
34713361

3472-
if (diagnoseTupleParameterMismatch(CCI, candidate.getParameters(),
3473-
CCI.CS.getType(argExpr), fnExpr, argExpr))
3474-
return true;
3475-
34763362
// We only handle structural errors here.
34773363
if (CCI.closeness != CC_ArgumentLabelMismatch &&
34783364
CCI.closeness != CC_ArgumentCountMismatch)

lib/Sema/CSDiagnostics.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,24 @@ FailureDiagnostic::getFunctionArgApplyInfo(ConstraintLocator *locator) const {
277277
fnInterfaceType, fnType, callee);
278278
}
279279

280+
Type FailureDiagnostic::restoreGenericParameters(
281+
Type type,
282+
llvm::function_ref<void(GenericTypeParamType *, Type)> substitution) {
283+
llvm::SmallPtrSet<GenericTypeParamType *, 4> processed;
284+
return type.transform([&](Type type) -> Type {
285+
if (auto *typeVar = type->getAs<TypeVariableType>()) {
286+
type = resolveType(typeVar);
287+
if (auto *GP = typeVar->getImpl().getGenericParameter()) {
288+
if (processed.insert(GP).second)
289+
substitution(GP, type);
290+
return GP;
291+
}
292+
}
293+
294+
return type;
295+
});
296+
}
297+
280298
Type RequirementFailure::getOwnerType() const {
281299
auto *anchor = getRawAnchor();
282300

@@ -3493,3 +3511,64 @@ bool MutatingMemberRefOnImmutableBase::diagnoseAsError() {
34933511
diagIDmember);
34943512
return failure.diagnoseAsError();
34953513
}
3514+
3515+
bool InvalidTupleSplatWithSingleParameterFailure::diagnoseAsError() {
3516+
auto *anchor = getRawAnchor();
3517+
3518+
auto selectedOverload = getChoiceFor(anchor);
3519+
if (!selectedOverload || !selectedOverload->choice.isDecl())
3520+
return false;
3521+
3522+
auto *choice = selectedOverload->choice.getDecl();
3523+
3524+
auto *argExpr = getArgumentExprFor(anchor);
3525+
if (!argExpr)
3526+
return false;
3527+
3528+
using Substitution = std::pair<GenericTypeParamType *, Type>;
3529+
llvm::SmallVector<Substitution, 8> substitutions;
3530+
3531+
auto paramTy = restoreGenericParameters(
3532+
ParamType, [&](GenericTypeParamType *GP, Type resolvedType) {
3533+
substitutions.push_back(std::make_pair(GP, resolvedType));
3534+
});
3535+
3536+
DeclBaseName name = choice->getBaseName();
3537+
3538+
std::string subsStr;
3539+
if (!substitutions.empty()) {
3540+
llvm::array_pod_sort(
3541+
substitutions.begin(), substitutions.end(),
3542+
[](const std::pair<GenericTypeParamType *, Type> *lhs,
3543+
const std::pair<GenericTypeParamType *, Type> *rhs) -> int {
3544+
GenericParamKey key1(lhs->first);
3545+
GenericParamKey key2(rhs->first);
3546+
return key1 < key2 ? -1 : (key1 == key2) ? 0 : 1;
3547+
});
3548+
3549+
subsStr += " [with ";
3550+
interleave(
3551+
substitutions,
3552+
[&subsStr](const Substitution &substitution) {
3553+
subsStr += substitution.first->getString();
3554+
subsStr += " = ";
3555+
subsStr += substitution.second->getString();
3556+
},
3557+
[&subsStr] { subsStr += ", "; });
3558+
subsStr += ']';
3559+
}
3560+
3561+
auto diagnostic =
3562+
name.isSpecial()
3563+
? emitDiagnostic(argExpr->getLoc(),
3564+
diag::single_tuple_parameter_mismatch_special,
3565+
choice->getDescriptiveKind(), paramTy, subsStr)
3566+
: emitDiagnostic(
3567+
argExpr->getLoc(), diag::single_tuple_parameter_mismatch_normal,
3568+
choice->getDescriptiveKind(), name, paramTy, subsStr);
3569+
3570+
diagnostic.highlight(argExpr->getSourceRange())
3571+
.fixItInsertAfter(argExpr->getStartLoc(), "(")
3572+
.fixItInsert(argExpr->getEndLoc(), ")");
3573+
return true;
3574+
}

lib/Sema/CSDiagnostics.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,13 @@ class FailureDiagnostic {
185185
Optional<FunctionArgApplyInfo>
186186
getFunctionArgApplyInfo(ConstraintLocator *locator) const;
187187

188+
/// \returns A new type with all of the type variables associated with
189+
/// generic parameters substituted back into being generic parameter type.
190+
Type restoreGenericParameters(
191+
Type type,
192+
llvm::function_ref<void(GenericTypeParamType *, Type)> substitution =
193+
[](GenericTypeParamType *, Type) {});
194+
188195
private:
189196
/// Compute anchor expression associated with current diagnostic.
190197
std::pair<Expr *, bool> computeAnchor() const;
@@ -1457,6 +1464,25 @@ class SkipUnhandledConstructInFunctionBuilderFailure final
14571464
bool diagnoseAsNote() override;
14581465
};
14591466

1467+
/// Diagnose situation when a single "tuple" parameter is given N arguments e.g.
1468+
///
1469+
/// ```swift
1470+
/// func foo<T>(_ x: (T, Bool)) {}
1471+
/// foo(1, false) // foo exptects a single argument of tuple type `(1, false)`
1472+
/// ```
1473+
class InvalidTupleSplatWithSingleParameterFailure final
1474+
: public FailureDiagnostic {
1475+
Type ParamType;
1476+
1477+
public:
1478+
InvalidTupleSplatWithSingleParameterFailure(Expr *root, ConstraintSystem &cs,
1479+
Type paramTy,
1480+
ConstraintLocator *locator)
1481+
: FailureDiagnostic(root, cs, locator), ParamType(paramTy) {}
1482+
1483+
bool diagnoseAsError() override;
1484+
};
1485+
14601486
/// Provides information about the application of a function argument to a
14611487
/// parameter.
14621488
class FunctionArgApplyInfo {

lib/Sema/CSFix.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,3 +657,43 @@ AllowMutatingMemberOnRValueBase::create(ConstraintSystem &cs, Type baseType,
657657
return new (cs.getAllocator())
658658
AllowMutatingMemberOnRValueBase(cs, baseType, member, name, locator);
659659
}
660+
661+
bool AllowTupleSplatForSingleParameter::diagnose(Expr *root, bool asNote) const {
662+
auto &cs = getConstraintSystem();
663+
InvalidTupleSplatWithSingleParameterFailure failure(root, cs, ParamType,
664+
getLocator());
665+
return failure.diagnose(asNote);
666+
}
667+
668+
bool AllowTupleSplatForSingleParameter::attempt(
669+
ConstraintSystem &cs, SmallVectorImpl<Param> &args, ArrayRef<Param> params,
670+
SmallVectorImpl<SmallVector<unsigned, 1>> &bindings,
671+
ConstraintLocatorBuilder locator) {
672+
if (params.size() != 1 || args.size() <= 1)
673+
return true;
674+
675+
const auto &param = params.front();
676+
677+
auto *paramTy = param.getOldType()->getAs<TupleType>();
678+
if (!paramTy || paramTy->getNumElements() != args.size())
679+
return true;
680+
681+
SmallVector<TupleTypeElt, 4> argElts;
682+
for (const auto &arg : args) {
683+
argElts.push_back(
684+
{arg.getPlainType(), arg.getLabel(), arg.getParameterFlags()});
685+
}
686+
687+
bindings[0].clear();
688+
bindings[0].push_back(0);
689+
690+
auto newArgType = TupleType::get(argElts, cs.getASTContext());
691+
692+
args.clear();
693+
args.push_back(AnyFunctionType::Param(newArgType, param.getLabel()));
694+
695+
auto *fix = new (cs.getAllocator()) AllowTupleSplatForSingleParameter(
696+
cs, paramTy, cs.getConstraintLocator(locator));
697+
698+
return cs.recordFix(fix);
699+
}

lib/Sema/CSFix.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ namespace constraints {
4040
class OverloadChoice;
4141
class ConstraintSystem;
4242
class ConstraintLocator;
43+
class ConstraintLocatorBuilder;
4344
class Solution;
4445

4546
/// Describes the kind of fix to apply to the given constraint before
@@ -183,6 +184,10 @@ enum class FixKind : uint8_t {
183184
/// Allow invalid reference to a member declared as `mutating`
184185
/// when base is an r-value type.
185186
AllowMutatingMemberOnRValueBase,
187+
188+
/// Allow a single tuple parameter to be matched with N arguments
189+
/// by forming all of the given arguments into a single tuple.
190+
AllowTupleSplatForSingleParameter,
186191
};
187192

188193
class ConstraintFix {
@@ -1181,6 +1186,32 @@ class SkipUnhandledConstructInFunctionBuilder final : public ConstraintFix {
11811186
NominalTypeDecl *builder, ConstraintLocator *locator);
11821187
};
11831188

1189+
class AllowTupleSplatForSingleParameter final : public ConstraintFix {
1190+
using Param = AnyFunctionType::Param;
1191+
1192+
Type ParamType;
1193+
1194+
AllowTupleSplatForSingleParameter(ConstraintSystem &cs, Type paramTy,
1195+
ConstraintLocator *locator)
1196+
: ConstraintFix(cs, FixKind::AllowTupleSplatForSingleParameter, locator),
1197+
ParamType(paramTy) {}
1198+
1199+
public:
1200+
std::string getName() const override {
1201+
return "allow single parameter tuple splat";
1202+
}
1203+
1204+
bool diagnose(Expr *root, bool asNote = false) const override;
1205+
1206+
/// Apply this fix to given arguments/parameters and return `true`
1207+
/// this fix is not applicable and solver can't continue, `false`
1208+
/// otherwise.
1209+
static bool attempt(ConstraintSystem &cs, SmallVectorImpl<Param> &args,
1210+
ArrayRef<Param> params,
1211+
SmallVectorImpl<SmallVector<unsigned, 1>> &bindings,
1212+
ConstraintLocatorBuilder locator);
1213+
};
1214+
11841215
} // end namespace constraints
11851216
} // end namespace swift
11861217

lib/Sema/CSSimplify.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -949,8 +949,14 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
949949
paramInfo,
950950
hasTrailingClosure,
951951
cs.shouldAttemptFixes(), listener,
952-
parameterBindings))
953-
return cs.getTypeMatchFailure(locator);
952+
parameterBindings)) {
953+
if (!cs.shouldAttemptFixes())
954+
return cs.getTypeMatchFailure(locator);
955+
956+
if (AllowTupleSplatForSingleParameter::attempt(cs, argsWithLabels, params,
957+
parameterBindings, locator))
958+
return cs.getTypeMatchFailure(locator);
959+
}
954960

955961
// If this application is part of an operator, then we allow an implicit
956962
// lvalue to be compatible with inout arguments. This is used by
@@ -6984,6 +6990,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
69846990
case FixKind::ExplicitlySpecifyGenericArguments:
69856991
case FixKind::GenericArgumentsMismatch:
69866992
case FixKind::AllowMutatingMemberOnRValueBase:
6993+
case FixKind::AllowTupleSplatForSingleParameter:
69876994
llvm_unreachable("handled elsewhere");
69886995
}
69896996

0 commit comments

Comments
 (0)