Skip to content

Commit 01dcc29

Browse files
authored
Merge pull request swiftlang#31809 from xedin/rdar-63230293
[ConstraintSystem] Detect and diagnose inability to infer type of closure parameter(s)
2 parents a70e606 + d111f11 commit 01dcc29

File tree

19 files changed

+203
-34
lines changed

19 files changed

+203
-34
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ ERROR(no_candidates_match_result_type,none,
250250
"no '%0' candidates produce the expected contextual result type %1",
251251
(StringRef, Type))
252252

253+
ERROR(cannot_infer_closure_parameter_type,none,
254+
"unable to infer type of a closure parameter %0 in the current context",
255+
(StringRef))
253256
ERROR(cannot_infer_closure_type,none,
254257
"unable to infer closure type in the current context", ())
255258
ERROR(cannot_infer_closure_result_type,none,

lib/Sema/CSBindings.cpp

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,32 +1087,27 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
10871087
// resolved and had to be bound to a placeholder "hole" type.
10881088
cs.increaseScore(SK_Hole);
10891089

1090+
ConstraintFix *fix = nullptr;
10901091
if (auto *GP = TypeVar->getImpl().getGenericParameter()) {
10911092
auto path = dstLocator->getPath();
10921093
// Drop `generic parameter` locator element so that all missing
10931094
// generic parameters related to the same path can be coalesced later.
1094-
auto *fix = DefaultGenericArgument::create(
1095+
fix = DefaultGenericArgument::create(
10951096
cs, GP,
10961097
cs.getConstraintLocator(dstLocator->getAnchor(), path.drop_back()));
1097-
if (cs.recordFix(fix))
1098-
return true;
1098+
} else if (TypeVar->getImpl().isClosureParameterType()) {
1099+
fix = SpecifyClosureParameterType::create(cs, dstLocator);
10991100
} else if (TypeVar->getImpl().isClosureResultType()) {
1100-
auto *fix = SpecifyClosureReturnType::create(
1101-
cs, TypeVar->getImpl().getLocator());
1102-
if (cs.recordFix(fix))
1103-
return true;
1101+
fix = SpecifyClosureReturnType::create(cs, dstLocator);
11041102
} else if (srcLocator->getAnchor() &&
11051103
isExpr<ObjectLiteralExpr>(srcLocator->getAnchor())) {
1106-
auto *fix = SpecifyObjectLiteralTypeImport::create(
1107-
cs, TypeVar->getImpl().getLocator());
1108-
if (cs.recordFix(fix))
1109-
return true;
1104+
fix = SpecifyObjectLiteralTypeImport::create(cs, dstLocator);
11101105
} else if (srcLocator->isKeyPathRoot()) {
1111-
auto *fix =
1112-
SpecifyKeyPathRootType::create(cs, TypeVar->getImpl().getLocator());
1113-
if (cs.recordFix(fix))
1114-
return true;
1106+
fix = SpecifyKeyPathRootType::create(cs, dstLocator);
11151107
}
1108+
1109+
if (fix && cs.recordFix(fix))
1110+
return true;
11161111
}
11171112
}
11181113

lib/Sema/CSDiagnostics.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@
4141
using namespace swift;
4242
using namespace constraints;
4343

44+
static bool hasFixFor(const Solution &solution, ConstraintLocator *locator) {
45+
return llvm::any_of(solution.Fixes, [&locator](const ConstraintFix *fix) {
46+
return fix->getLocator() == locator;
47+
});
48+
}
49+
4450
FailureDiagnostic::~FailureDiagnostic() {}
4551

4652
bool FailureDiagnostic::diagnose(bool asNote) {
@@ -6125,6 +6131,75 @@ bool MissingContextualBaseInMemberRefFailure::diagnoseAsError() {
61256131
return true;
61266132
}
61276133

6134+
bool UnableToInferClosureParameterType::diagnoseAsError() {
6135+
auto *closure = castToExpr<ClosureExpr>(getRawAnchor());
6136+
6137+
// Let's check whether this closure is an argument to
6138+
// a call which couldn't be properly resolved e.g.
6139+
// missing member or invalid contextual reference and
6140+
// if so let's not diagnose this problem because main
6141+
// issue here is inability to establish context for
6142+
// closure inference.
6143+
//
6144+
// TODO(diagnostics): Once we gain an ability to determine
6145+
// originating source of type holes this check could be
6146+
// significantly simplified.
6147+
{
6148+
auto &solution = getSolution();
6149+
6150+
// If there is a contextual mismatch associated with this
6151+
// closure, let's not diagnose any parameter type issues.
6152+
if (hasFixFor(solution, getConstraintLocator(
6153+
closure, LocatorPathElt::ContextualType())))
6154+
return false;
6155+
6156+
if (auto *parentExpr = findParentExpr(closure)) {
6157+
while (parentExpr &&
6158+
(isa<TupleExpr>(parentExpr) || isa<ParenExpr>(parentExpr))) {
6159+
parentExpr = findParentExpr(parentExpr);
6160+
}
6161+
6162+
if (parentExpr) {
6163+
// Missing or invalid member reference in call.
6164+
if (auto *AE = dyn_cast<ApplyExpr>(parentExpr)) {
6165+
if (getType(AE->getFn())->isHole())
6166+
return false;
6167+
}
6168+
6169+
// Any fix anchored on parent expression makes it unnecessary
6170+
// to diagnose unability to infer parameter type because it's
6171+
// an indication that proper context couldn't be established to
6172+
// resolve the closure.
6173+
ASTNode parentNode(parentExpr);
6174+
if (llvm::any_of(solution.Fixes,
6175+
[&parentNode](const ConstraintFix *fix) -> bool {
6176+
return fix->getAnchor() == parentNode;
6177+
}))
6178+
return false;
6179+
}
6180+
}
6181+
}
6182+
6183+
auto paramIdx = getLocator()
6184+
->castLastElementTo<LocatorPathElt::TupleElement>()
6185+
.getIndex();
6186+
6187+
auto *PD = closure->getParameters()->get(paramIdx);
6188+
6189+
llvm::SmallString<16> id;
6190+
llvm::raw_svector_ostream OS(id);
6191+
6192+
if (PD->isAnonClosureParam()) {
6193+
OS << "$" << paramIdx;
6194+
} else {
6195+
OS << "'" << PD->getParameterName() << "'";
6196+
}
6197+
6198+
auto loc = PD->isAnonClosureParam() ? getLoc() : PD->getLoc();
6199+
emitDiagnosticAt(loc, diag::cannot_infer_closure_parameter_type, OS.str());
6200+
return true;
6201+
}
6202+
61286203
bool UnableToInferClosureReturnType::diagnoseAsError() {
61296204
auto *closure = castToExpr<ClosureExpr>(getRawAnchor());
61306205

lib/Sema/CSDiagnostics.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1971,6 +1971,15 @@ class MissingContextualBaseInMemberRefFailure final : public FailureDiagnostic {
19711971
bool diagnoseAsError();
19721972
};
19731973

1974+
class UnableToInferClosureParameterType final : public FailureDiagnostic {
1975+
public:
1976+
UnableToInferClosureParameterType(const Solution &solution,
1977+
ConstraintLocator *locator)
1978+
: FailureDiagnostic(solution, locator) {}
1979+
1980+
bool diagnoseAsError();
1981+
};
1982+
19741983
class UnableToInferClosureReturnType final : public FailureDiagnostic {
19751984
public:
19761985
UnableToInferClosureReturnType(const Solution &solution,

lib/Sema/CSFix.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "ConstraintSystem.h"
2323
#include "OverloadChoice.h"
2424
#include "swift/AST/Expr.h"
25+
#include "swift/AST/ParameterList.h"
2526
#include "swift/AST/Type.h"
2627
#include "swift/AST/Types.h"
2728
#include "swift/Basic/SourceManager.h"
@@ -1229,6 +1230,38 @@ SpecifyBaseTypeForContextualMember *SpecifyBaseTypeForContextualMember::create(
12291230
SpecifyBaseTypeForContextualMember(cs, member, locator);
12301231
}
12311232

1233+
std::string SpecifyClosureParameterType::getName() const {
1234+
std::string name;
1235+
llvm::raw_string_ostream OS(name);
1236+
1237+
auto *closure = castToExpr<ClosureExpr>(getAnchor());
1238+
auto paramLoc =
1239+
getLocator()->castLastElementTo<LocatorPathElt::TupleElement>();
1240+
1241+
auto *PD = closure->getParameters()->get(paramLoc.getIndex());
1242+
1243+
OS << "specify type for parameter ";
1244+
if (PD->isAnonClosureParam()) {
1245+
OS << "$" << paramLoc.getIndex();
1246+
} else {
1247+
OS << "'" << PD->getParameterName() << "'";
1248+
}
1249+
1250+
return OS.str();
1251+
}
1252+
1253+
bool SpecifyClosureParameterType::diagnose(const Solution &solution,
1254+
bool asNote) const {
1255+
UnableToInferClosureParameterType failure(solution, getLocator());
1256+
return failure.diagnose(asNote);
1257+
}
1258+
1259+
SpecifyClosureParameterType *
1260+
SpecifyClosureParameterType::create(ConstraintSystem &cs,
1261+
ConstraintLocator *locator) {
1262+
return new (cs.getAllocator()) SpecifyClosureParameterType(cs, locator);
1263+
}
1264+
12321265
bool SpecifyClosureReturnType::diagnose(const Solution &solution,
12331266
bool asNote) const {
12341267
UnableToInferClosureReturnType failure(solution, getLocator());

lib/Sema/CSFix.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,10 @@ enum class FixKind : uint8_t {
234234
/// inferred and has to be specified explicitly.
235235
SpecifyBaseTypeForContextualMember,
236236

237+
/// Type of the closure parameter used in the body couldn't be inferred
238+
/// and has to be specified explicitly.
239+
SpecifyClosureParameterType,
240+
237241
/// Closure return type has to be explicitly specified because it can't be
238242
/// inferred in current context e.g. because it's a multi-statement closure.
239243
SpecifyClosureReturnType,
@@ -253,7 +257,7 @@ enum class FixKind : uint8_t {
253257

254258
/// A warning fix that allows a coercion to perform a force-cast.
255259
AllowCoercionToForceCast,
256-
260+
257261
/// Allow key path root type mismatch when applying a key path that has a
258262
/// root type not convertible to the type of the base instance.
259263
AllowKeyPathRootTypeMismatch,
@@ -1712,6 +1716,19 @@ class SpecifyBaseTypeForContextualMember final : public ConstraintFix {
17121716
create(ConstraintSystem &cs, DeclNameRef member, ConstraintLocator *locator);
17131717
};
17141718

1719+
class SpecifyClosureParameterType final : public ConstraintFix {
1720+
SpecifyClosureParameterType(ConstraintSystem &cs, ConstraintLocator *locator)
1721+
: ConstraintFix(cs, FixKind::SpecifyClosureParameterType, locator) {}
1722+
1723+
public:
1724+
std::string getName() const;
1725+
1726+
bool diagnose(const Solution &solution, bool asNote = false) const;
1727+
1728+
static SpecifyClosureParameterType *create(ConstraintSystem &cs,
1729+
ConstraintLocator *locator);
1730+
};
1731+
17151732
class SpecifyClosureReturnType final : public ConstraintFix {
17161733
SpecifyClosureReturnType(ConstraintSystem &cs, ConstraintLocator *locator)
17171734
: ConstraintFix(cs, FixKind::SpecifyClosureReturnType, locator) {}

lib/Sema/CSGen.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2191,8 +2191,12 @@ namespace {
21912191
auto declaredTy = param->getType();
21922192
externalType = CS.openUnboundGenericType(declaredTy, paramLoc);
21932193
} else {
2194+
// Let's allow parameters which haven't been explicitly typed
2195+
// to become holes by default, this helps in situations like
2196+
// `foo { a in }` where `foo` doesn't exist.
21942197
externalType = CS.createTypeVariable(
2195-
paramLoc, TVO_CanBindToInOut | TVO_CanBindToNoEscape);
2198+
paramLoc,
2199+
TVO_CanBindToInOut | TVO_CanBindToNoEscape | TVO_CanBindToHole);
21962200
}
21972201

21982202
closureParams.push_back(param->toFunctionParam(externalType));

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9609,6 +9609,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
96099609
case FixKind::AllowTupleSplatForSingleParameter:
96109610
case FixKind::AllowInvalidUseOfTrailingClosure:
96119611
case FixKind::AllowNonClassTypeToConvertToAnyObject:
9612+
case FixKind::SpecifyClosureParameterType:
96129613
case FixKind::SpecifyClosureReturnType:
96139614
case FixKind::AddQualifierToAccessTopLevelName:
96149615
llvm_unreachable("handled elsewhere");

lib/Sema/ConstraintGraph.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,10 @@ bool ConstraintGraph::contractEdges() {
11261126
if (isParamBindingConstraint && tyvar1->getImpl().canBindToInOut()) {
11271127
bool isNotContractable = true;
11281128
if (auto bindings = CS.getPotentialBindings(tyvar1)) {
1129+
// Holes can't be contracted.
1130+
if (bindings.IsHole)
1131+
continue;
1132+
11291133
for (auto &binding : bindings.Bindings) {
11301134
auto type = binding.BindingType;
11311135
isNotContractable = type.findIf([&](Type nestedType) -> bool {

lib/Sema/ConstraintSystem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,10 @@ class TypeVariableType::Implementation {
301301
/// Determine whether this type variable represents a closure type.
302302
bool isClosureType() const;
303303

304+
/// Determine whether this type variable represents one of the
305+
/// parameter types associated with a closure.
306+
bool isClosureParameterType() const;
307+
304308
/// Determine whether this type variable represents a closure result type.
305309
bool isClosureResultType() const;
306310

0 commit comments

Comments
 (0)