Skip to content

Commit 3d1ab4d

Browse files
authored
Merge pull request swiftlang#28712 from hborla/function-parameter-mismatch-diagnostics
[ConstraintSystem] Port function parameter type mismatch diagnostics.
2 parents 3a24f59 + 51c7c8c commit 3d1ab4d

File tree

8 files changed

+144
-143
lines changed

8 files changed

+144
-143
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 0 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,6 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
249249
Optional<std::function<bool(ArrayRef<OverloadChoice>)>> callback = None,
250250
bool includeInaccessibleMembers = true);
251251

252-
bool diagnoseTrailingClosureErrors(ApplyExpr *expr);
253-
254252
bool diagnoseSubscriptErrors(SubscriptExpr *SE, bool performingSet);
255253

256254
bool visitExpr(Expr *E);
@@ -1710,113 +1708,6 @@ static bool diagnoseClosureExplicitParameterMismatch(
17101708
return false;
17111709
}
17121710

1713-
bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) {
1714-
if (!callExpr->hasTrailingClosure())
1715-
return false;
1716-
1717-
auto *fnExpr = callExpr->getFn();
1718-
auto *argExpr = callExpr->getArg();
1719-
1720-
ClosureExpr *closureExpr = nullptr;
1721-
if (auto *PE = dyn_cast<ParenExpr>(argExpr)) {
1722-
closureExpr = dyn_cast<ClosureExpr>(PE->getSubExpr());
1723-
} else {
1724-
return false;
1725-
}
1726-
1727-
if (!closureExpr)
1728-
return false;
1729-
1730-
class CallResultListener : public ExprTypeCheckListener {
1731-
Type expectedResultType;
1732-
1733-
public:
1734-
explicit CallResultListener(Type resultType)
1735-
: expectedResultType(resultType) {}
1736-
1737-
bool builtConstraints(ConstraintSystem &cs, Expr *expr) override {
1738-
if (!expectedResultType)
1739-
return false;
1740-
1741-
auto resultType = cs.getType(expr);
1742-
auto *locator = cs.getConstraintLocator(expr);
1743-
1744-
// Since we know that this is trailing closure, format of the
1745-
// type could be like this - ((Input) -> Result) -> ClosureResult
1746-
// which we can leverage to create specific conversion for
1747-
// result type of the call itself, this might help us gain
1748-
// some valuable contextual information.
1749-
if (auto *fnType = resultType->getAs<AnyFunctionType>()) {
1750-
cs.addConstraint(ConstraintKind::Conversion, fnType->getResult(),
1751-
expectedResultType, locator);
1752-
} else if (auto *typeVar = resultType->getAs<TypeVariableType>()) {
1753-
auto tv = cs.createTypeVariable(cs.getConstraintLocator(expr),
1754-
TVO_CanBindToLValue |
1755-
TVO_PrefersSubtypeBinding |
1756-
TVO_CanBindToNoEscape);
1757-
1758-
auto extInfo = FunctionType::ExtInfo().withThrows();
1759-
1760-
FunctionType::Param tvParam(tv);
1761-
auto fTy = FunctionType::get({tvParam}, expectedResultType, extInfo);
1762-
1763-
// Add a conversion constraint between the types.
1764-
cs.addConstraint(ConstraintKind::Conversion, typeVar, fTy, locator,
1765-
/*isFavored*/ true);
1766-
}
1767-
1768-
return false;
1769-
}
1770-
};
1771-
1772-
SmallPtrSet<TypeBase *, 4> possibleTypes;
1773-
auto currentType = CS.simplifyType(CS.getType(fnExpr));
1774-
1775-
// If current type has type variables or unresolved types
1776-
// let's try to re-typecheck it to see if we can get some
1777-
// more information about what is going on.
1778-
if (currentType->hasTypeVariable() || currentType->hasUnresolvedType()) {
1779-
auto contextualType = CS.getContextualType();
1780-
CallResultListener listener(contextualType);
1781-
getPossibleTypesOfExpressionWithoutApplying(
1782-
fnExpr, CS.DC, possibleTypes, FreeTypeVariableBinding::UnresolvedType,
1783-
&listener);
1784-
1785-
// Looks like there is there a contextual mismatch
1786-
// related to function type, let's try to diagnose it.
1787-
if (possibleTypes.empty() && contextualType &&
1788-
!contextualType->hasUnresolvedType())
1789-
return diagnoseContextualConversionError(callExpr, contextualType,
1790-
CS.getContextualTypePurpose());
1791-
} else {
1792-
possibleTypes.insert(currentType.getPointer());
1793-
}
1794-
1795-
for (Type type : possibleTypes) {
1796-
auto *fnType = type->getAs<AnyFunctionType>();
1797-
if (!fnType)
1798-
continue;
1799-
1800-
auto params = fnType->getParams();
1801-
if (params.size() != 1)
1802-
return false;
1803-
1804-
Type paramType = params.front().getOldType();
1805-
if (auto paramFnType = paramType->getAs<AnyFunctionType>()) {
1806-
auto closureType = CS.getType(closureExpr);
1807-
if (auto *argFnType = closureType->getAs<AnyFunctionType>()) {
1808-
auto *params = closureExpr->getParameters();
1809-
auto loc = params ? params->getStartLoc() : closureExpr->getStartLoc();
1810-
if (diagnoseClosureExplicitParameterMismatch(
1811-
CS, loc, argFnType->getParams(), paramFnType->getParams()))
1812-
return true;
1813-
}
1814-
}
1815-
}
1816-
1817-
return false;
1818-
}
1819-
18201711
/// Check if there failure associated with expression is related
18211712
/// to given contextual type.
18221713
bool FailureDiagnosis::diagnoseCallContextualConversionErrors(
@@ -1910,13 +1801,6 @@ static bool isViableOverloadSet(const CalleeCandidateInfo &CCI,
19101801
}
19111802

19121803
bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
1913-
// If this call involves trailing closure as an argument,
1914-
// let's treat it specially, because re-typecheck of the
1915-
// either function or arguments might results in diagnosing
1916-
// of the unrelated problems due to luck of context.
1917-
if (diagnoseTrailingClosureErrors(callExpr))
1918-
return true;
1919-
19201804
if (diagnoseCallContextualConversionErrors(callExpr, CS.getContextualType(),
19211805
CS.getContextualTypePurpose()))
19221806
return true;

lib/Sema/CSDiagnostics.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2826,6 +2826,16 @@ bool TupleContextualFailure::diagnoseAsError() {
28262826
return true;
28272827
}
28282828

2829+
bool FunctionTypeMismatch::diagnoseAsError() {
2830+
auto purpose = getContextualTypePurpose();
2831+
auto diagnostic = getDiagnosticFor(purpose, /*forProtocol=*/false);
2832+
if (!diagnostic)
2833+
return false;
2834+
2835+
emitDiagnostic(getAnchor()->getLoc(), *diagnostic, getFromType(), getToType());
2836+
return true;
2837+
}
2838+
28292839
bool AutoClosureForwardingFailure::diagnoseAsError() {
28302840
auto *loc = getLocator();
28312841
auto last = loc->castLastElementTo<LocatorPathElt::ApplyArgToParam>();

lib/Sema/CSDiagnostics.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,23 @@ class TupleContextualFailure final : public ContextualFailure {
852852
}
853853
};
854854

855+
class FunctionTypeMismatch final : public ContextualFailure {
856+
/// Indices of the parameters whose types do not match.
857+
llvm::SmallVector<unsigned, 4> Indices;
858+
859+
public:
860+
FunctionTypeMismatch(ConstraintSystem &cs, ContextualTypePurpose purpose,
861+
Type lhs, Type rhs, llvm::ArrayRef<unsigned> indices,
862+
ConstraintLocator *locator)
863+
: ContextualFailure(cs, purpose, lhs, rhs, locator),
864+
Indices(indices.begin(), indices.end()) {
865+
std::sort(Indices.begin(), Indices.end());
866+
assert(getFromType()->is<AnyFunctionType>() && getToType()->is<AnyFunctionType>());
867+
}
868+
869+
bool diagnoseAsError() override;
870+
};
871+
855872
/// Diagnose situations when @autoclosure argument is passed to @autoclosure
856873
/// parameter directly without calling it first.
857874
class AutoClosureForwardingFailure final : public FailureDiagnostic {

lib/Sema/CSFix.cpp

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,35 @@ ContextualMismatch *ContextualMismatch::create(ConstraintSystem &cs, Type lhs,
255255
return new (cs.getAllocator()) ContextualMismatch(cs, lhs, rhs, locator);
256256
}
257257

258+
/// Computes the contextual type information for a type mismatch of a
259+
/// component in a structural type (tuple or function type).
260+
///
261+
/// \returns A tuple containing the contextual type purpose, the source type,
262+
/// and the contextual type.
263+
static Optional<std::tuple<ContextualTypePurpose, Type, Type>>
264+
getStructuralTypeContext(ConstraintSystem &cs, ConstraintLocator *locator) {
265+
if (auto contextualType = cs.getContextualType()) {
266+
if (auto *anchor = simplifyLocatorToAnchor(locator))
267+
return std::make_tuple(cs.getContextualTypePurpose(),
268+
cs.getType(anchor),
269+
contextualType);
270+
} else if (auto argApplyInfo = cs.getFunctionArgApplyInfo(locator)) {
271+
return std::make_tuple(CTP_CallArgument,
272+
argApplyInfo->getArgType(),
273+
argApplyInfo->getParamType());
274+
} else if (auto *coerceExpr = dyn_cast<CoerceExpr>(locator->getAnchor())) {
275+
return std::make_tuple(CTP_CoerceOperand,
276+
cs.getType(coerceExpr->getSubExpr()),
277+
cs.getType(coerceExpr));
278+
} else if (auto *assignExpr = dyn_cast<AssignExpr>(locator->getAnchor())) {
279+
return std::make_tuple(CTP_AssignSource,
280+
cs.getType(assignExpr->getSrc()),
281+
cs.getType(assignExpr->getDest()));
282+
}
283+
284+
return None;
285+
}
286+
258287
bool AllowTupleTypeMismatch::coalesceAndDiagnose(
259288
ArrayRef<ConstraintFix *> fixes, bool asNote) const {
260289
llvm::SmallVector<unsigned, 4> indices;
@@ -270,31 +299,16 @@ bool AllowTupleTypeMismatch::coalesceAndDiagnose(
270299

271300
auto &cs = getConstraintSystem();
272301
auto *locator = getLocator();
273-
auto purpose = cs.getContextualTypePurpose();
302+
ContextualTypePurpose purpose;
274303
Type fromType;
275304
Type toType;
276305

277306
if (getFromType()->is<TupleType>() && getToType()->is<TupleType>()) {
307+
purpose = cs.getContextualTypePurpose();
278308
fromType = getFromType();
279309
toType = getToType();
280-
} else if (auto contextualType = cs.getContextualType()) {
281-
auto *tupleExpr = simplifyLocatorToAnchor(locator);
282-
if (!tupleExpr)
283-
return false;
284-
fromType = cs.getType(tupleExpr);
285-
toType = contextualType;
286-
} else if (auto argApplyInfo = cs.getFunctionArgApplyInfo(locator)) {
287-
purpose = CTP_CallArgument;
288-
fromType = argApplyInfo->getArgType();
289-
toType = argApplyInfo->getParamType();
290-
} else if (auto *coerceExpr = dyn_cast<CoerceExpr>(locator->getAnchor())) {
291-
purpose = CTP_CoerceOperand;
292-
fromType = cs.getType(coerceExpr->getSubExpr());
293-
toType = cs.getType(coerceExpr);
294-
} else if (auto *assignExpr = dyn_cast<AssignExpr>(locator->getAnchor())) {
295-
purpose = CTP_AssignSource;
296-
fromType = cs.getType(assignExpr->getSrc());
297-
toType = cs.getType(assignExpr->getDest());
310+
} else if (auto contextualTypeInfo = getStructuralTypeContext(cs, locator)) {
311+
std::tie(purpose, fromType, toType) = *contextualTypeInfo;
298312
} else {
299313
return false;
300314
}
@@ -315,6 +329,41 @@ AllowTupleTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs,
315329
AllowTupleTypeMismatch(cs, lhs, rhs, locator, index);
316330
}
317331

332+
bool AllowFunctionTypeMismatch::coalesceAndDiagnose(
333+
ArrayRef<ConstraintFix *> fixes, bool asNote) const {
334+
llvm::SmallVector<unsigned, 4> indices{ParamIndex};
335+
336+
for (auto fix : fixes) {
337+
if (auto *fnFix = fix->getAs<AllowFunctionTypeMismatch>())
338+
indices.push_back(fnFix->ParamIndex);
339+
}
340+
341+
auto &cs = getConstraintSystem();
342+
auto *locator = getLocator();
343+
ContextualTypePurpose purpose;
344+
Type fromType;
345+
Type toType;
346+
347+
auto contextualTypeInfo = getStructuralTypeContext(cs, locator);
348+
if (!contextualTypeInfo)
349+
return false;
350+
351+
std::tie(purpose, fromType, toType) = *contextualTypeInfo;
352+
FunctionTypeMismatch failure(cs, purpose, fromType, toType, indices, locator);
353+
return failure.diagnose(asNote);
354+
}
355+
356+
bool AllowFunctionTypeMismatch::diagnose(bool asNote) const {
357+
return coalesceAndDiagnose({}, asNote);
358+
}
359+
360+
AllowFunctionTypeMismatch *
361+
AllowFunctionTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs,
362+
ConstraintLocator *locator, unsigned index) {
363+
return new (cs.getAllocator())
364+
AllowFunctionTypeMismatch(cs, lhs, rhs, locator, index);
365+
}
366+
318367
bool GenericArgumentsMismatch::diagnose(bool asNote) const {
319368
auto &cs = getConstraintSystem();
320369
GenericArgumentsMismatchFailure failure(cs, getFromType(), getToType(),

lib/Sema/CSFix.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ enum class FixKind : uint8_t {
145145
/// types.
146146
AllowTupleTypeMismatch,
147147

148+
/// Allow a function type to be destructured with mismatched parameter types
149+
/// or return type.
150+
AllowFunctionTypeMismatch,
151+
148152
/// Allow an invalid member access on a value of protocol type as if
149153
/// that protocol type were a generic constraint requiring conformance
150154
/// to that protocol.
@@ -987,6 +991,35 @@ class AllowTupleTypeMismatch final : public ContextualMismatch {
987991
bool diagnose(bool asNote = false) const override;
988992
};
989993

994+
class AllowFunctionTypeMismatch final : public ContextualMismatch {
995+
/// The index of the parameter where the type mismatch occurred.
996+
unsigned ParamIndex;
997+
998+
AllowFunctionTypeMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
999+
ConstraintLocator *locator, unsigned index)
1000+
: ContextualMismatch(cs, FixKind::AllowFunctionTypeMismatch, lhs, rhs,
1001+
locator), ParamIndex(index) {}
1002+
1003+
public:
1004+
static AllowFunctionTypeMismatch *create(ConstraintSystem &cs, Type lhs,
1005+
Type rhs, ConstraintLocator *locator,
1006+
unsigned index);
1007+
1008+
static bool classof(const ConstraintFix *fix) {
1009+
return fix->getKind() == FixKind::AllowFunctionTypeMismatch;
1010+
}
1011+
1012+
std::string getName() const override {
1013+
return "allow function type mismatch";
1014+
}
1015+
1016+
bool coalesceAndDiagnose(ArrayRef<ConstraintFix *> secondaryFixes,
1017+
bool asNote = false) const override;
1018+
1019+
bool diagnose(bool asNote = false) const override;
1020+
};
1021+
1022+
9901023
class AllowMutatingMemberOnRValueBase final : public AllowInvalidMemberRef {
9911024
AllowMutatingMemberOnRValueBase(ConstraintSystem &cs, Type baseType,
9921025
ValueDecl *member, DeclNameRef name,

lib/Sema/CSSimplify.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3543,19 +3543,21 @@ bool ConstraintSystem::repairFailures(
35433543

35443544
// Drop the `tuple element` locator element so that all tuple element
35453545
// mismatches within the same tuple type can be coalesced later.
3546+
auto index = elt.getAs<LocatorPathElt::TupleElement>()->getIndex();
35463547
path.pop_back();
35473548
auto *tupleLocator = getConstraintLocator(locator.getAnchor(), path);
35483549

3549-
// TODO: Add a tailored fix for function parameter type mismatches.
35503550
// Let this fail if it's a contextual mismatch with sequence element types,
35513551
// as there's a special fix for that.
3552-
if (tupleLocator->isLastElement<LocatorPathElt::FunctionArgument>() ||
3553-
tupleLocator->isLastElement<LocatorPathElt::SequenceElementType>())
3552+
if (tupleLocator->isLastElement<LocatorPathElt::SequenceElementType>())
35543553
break;
35553554

3556-
auto index = elt.getAs<LocatorPathElt::TupleElement>()->getIndex();
3557-
auto *fix =
3558-
AllowTupleTypeMismatch::create(*this, lhs, rhs, tupleLocator, index);
3555+
ConstraintFix *fix;
3556+
if (tupleLocator->isLastElement<LocatorPathElt::FunctionArgument>()) {
3557+
fix = AllowFunctionTypeMismatch::create(*this, lhs, rhs, tupleLocator, index);
3558+
} else {
3559+
fix = AllowTupleTypeMismatch::create(*this, lhs, rhs, tupleLocator, index);
3560+
}
35593561
conversionsOrFixes.push_back(fix);
35603562
break;
35613563
}
@@ -8533,6 +8535,12 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
85338535
return matchTupleTypes(matchingType, smaller, matchKind, subflags, locator);
85348536
}
85358537

8538+
case FixKind::AllowFunctionTypeMismatch: {
8539+
if (recordFix(fix, /*impact=*/5))
8540+
return SolutionKind::Error;
8541+
return SolutionKind::Solved;
8542+
}
8543+
85368544
case FixKind::TreatEphemeralAsNonEphemeral: {
85378545
auto *theFix = static_cast<TreatEphemeralAsNonEphemeral *>(fix);
85388546
// If we have a non-ephemeral locator for an ephemeral conversion, make a

test/Constraints/trailing_closures_objc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ func foo(options: [AVMediaSelectionOption]) {
1616

1717
func rdar28004686(a: [IndexPath]) {
1818
_ = a.sorted { (lhs: NSIndexPath, rhs: NSIndexPath) -> Bool in true }
19-
// expected-error@-1 {{'NSIndexPath' is not convertible to 'IndexPath'}}
19+
// expected-error@-1 {{cannot convert value of type '(NSIndexPath, NSIndexPath) -> Bool' to expected argument type '(IndexPath, IndexPath) throws -> Bool'}}
2020
}
2121

2222
class Test: NSObject {

test/expr/closure/closures.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func funcdecl5(_ a: Int, _ y: Int) {
7272
func6({a,b in 4.0 }) // expected-error {{cannot convert value of type 'Double' to closure result type 'Int'}}
7373

7474
// TODO: This diagnostic can be improved: rdar://22128205
75-
func6({(a : Float, b) in 4 }) // expected-error {{cannot convert value of type '(Float, _) -> Int' to expected argument type '(Int, Int) -> Int'}}
75+
func6({(a : Float, b) in 4 }) // expected-error {{cannot convert value of type '(Float, Int) -> Int' to expected argument type '(Int, Int) -> Int'}}
7676

7777

7878

0 commit comments

Comments
 (0)