Skip to content

Commit 01dcc29

Browse files
authored
Merge pull request #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

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ bool TypeVariableType::Implementation::isClosureType() const {
9090
return isExpr<ClosureExpr>(locator->getAnchor()) && locator->getPath().empty();
9191
}
9292

93+
bool TypeVariableType::Implementation::isClosureParameterType() const {
94+
if (!(locator && locator->getAnchor()))
95+
return false;
96+
97+
return isExpr<ClosureExpr>(locator->getAnchor()) &&
98+
locator->isLastElement<LocatorPathElt::TupleElement>();
99+
}
100+
93101
bool TypeVariableType::Implementation::isClosureResultType() const {
94102
if (!(locator && locator->getAnchor()))
95103
return false;

test/Constraints/closures.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ struct S_3520 {
504504
func sr3520_set_via_closure<S, T>(_ closure: (inout S, T) -> ()) {} // expected-note {{in call to function 'sr3520_set_via_closure'}}
505505
sr3520_set_via_closure({ $0.number1 = $1 })
506506
// expected-error@-1 {{generic parameter 'S' could not be inferred}}
507-
// expected-error@-2 {{generic parameter 'T' could not be inferred}}
507+
// expected-error@-2 {{unable to infer type of a closure parameter $1 in the current context}}
508508

509509
// SR-3073: UnresolvedDotExpr in single expression closure
510510

@@ -1017,3 +1017,21 @@ func overloaded_with_default_and_autoclosure<T>(b: Int = 0, c: @escaping () -> T
10171017

10181018
overloaded_with_default_and_autoclosure { 42 } // Ok
10191019
overloaded_with_default_and_autoclosure(42) // Ok
1020+
1021+
// SR-12815 - `error: type of expression is ambiguous without more context` in many cases where methods are missing
1022+
func sr12815() {
1023+
let _ = { a, b in }
1024+
// expected-error@-1 {{unable to infer type of a closure parameter 'a' in the current context}}
1025+
// expected-error@-2 {{unable to infer type of a closure parameter 'b' in the current context}}
1026+
1027+
_ = .a { b in } // expected-error {{cannot infer contextual base in reference to member 'a'}}
1028+
1029+
struct S {}
1030+
1031+
func test(s: S) {
1032+
S.doesntExist { b in } // expected-error {{type 'S' has no member 'doesntExist'}}
1033+
s.doesntExist { b in } // expected-error {{value of type 'S' has no member 'doesntExist'}}
1034+
s.doesntExist1 { v in } // expected-error {{value of type 'S' has no member 'doesntExist1'}}
1035+
.doesntExist2() { $0 }
1036+
}
1037+
}

test/Constraints/diagnostics.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,7 @@ struct Toe {
255255
let toenail: Nail // expected-error {{cannot find type 'Nail' in scope}}
256256

257257
func clip() {
258-
// TODO(diagnostics): Solver should stop once it has detected that `toenail` doesn't exist and report that.
259-
toenail.inspect { x in // expected-error {{type of expression is ambiguous without more context}}
258+
toenail.inspect { x in
260259
toenail.inspect { y in }
261260
}
262261
}
@@ -297,7 +296,7 @@ func r18800223(_ i : Int) {
297296
}
298297

299298
// <rdar://problem/21883806> Bogus "'_' can only appear in a pattern or on the left side of an assignment" is back
300-
_ = { $0 } // expected-error {{unable to infer closure type in the current context}}
299+
_ = { $0 } // expected-error {{unable to infer type of a closure parameter $0 in the current context}}
301300

302301

303302

test/Sema/diag_ambiguous_overloads.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ fe(.baz) // expected-error {{reference to member 'baz' cannot be resolved withou
1515
fe(.nope, .nyet) // expected-error {{type 'Int' has no member 'nope'}}
1616
// expected-error@-1 {{reference to member 'nyet' cannot be resolved without a contextual type}}
1717

18-
func fg<T>(_ f: (T) -> T) -> Void {} // expected-note {{in call to function 'fg'}}
19-
fg({x in x}) // expected-error {{generic parameter 'T' could not be inferred}}
18+
func fg<T>(_ f: (T) -> T) -> Void {}
19+
fg({x in x}) // expected-error {{unable to infer type of a closure parameter 'x' in the current context}}
2020

2121

2222
struct S {
23-
func f<T>(_ i: (T) -> T, _ j: Int) -> Void {} // expected-note {{in call to function 'f'}}
23+
func f<T>(_ i: (T) -> T, _ j: Int) -> Void {}
2424
func f(_ d: (Double) -> Double) -> Void {}
2525
func test() -> Void {
26-
f({x in x}, 2) // expected-error {{generic parameter 'T' could not be inferred}}
26+
f({x in x}, 2) // expected-error {{unable to infer type of a closure parameter 'x' in the current context}}
2727
}
2828

2929
func g<T>(_ a: T, _ b: Int) -> Void {}

test/decl/typealias/generic.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ typealias E<T1, T2> = Int // expected-note {{generic type 'E' declared here}}
6868
// expected-note@-1 {{'T1' declared as parameter to type 'E'}}
6969
// expected-note@-2 {{'T2' declared as parameter to type 'E'}}
7070

71-
typealias F<T1, T2> = (T1) -> T2 // expected-note {{'T1' declared as parameter to type 'F'}}
71+
typealias F<T1, T2> = (T1) -> T2
7272

7373
// Type alias of type alias.
7474
typealias G<S1, S2> = A<S1, S2>
@@ -94,7 +94,7 @@ let _ : D<Int, Int, Float> = D(a: 1, b: 2)
9494

9595
let _ : F = { (a : Int) -> Int in a } // Infer the types of F
9696

97-
let _ : F = { a in a } // expected-error {{generic parameter 'T1' could not be inferred}}
97+
let _ : F = { a in a } // expected-error {{unable to infer type of a closure parameter 'a' in the current context}}
9898

9999
_ = MyType(a: "foo", b: 42)
100100
_ = A(a: "foo", b: 42)

0 commit comments

Comments
 (0)