Skip to content

Commit b07f7b3

Browse files
committed
[CS] Improve handling of holes for Named/AnyPatterns
Rather than eagerly binding them to holes if the sequence element type ends up being Any, let's record the CollectionElementContextualMismatch fix, and then if the patterns end up becoming holes, skip penalizing them if we know the fix was recorded. This avoids prematurely turning type variables for ExprPatterns into holes, which should be able to get better bindings from the expression provided. Also this means we'll apply the logic to non-Any sequence types, which previously we would give a confusing diagnostic to.
1 parent 1be2589 commit b07f7b3

File tree

7 files changed

+43
-22
lines changed

7 files changed

+43
-22
lines changed

include/swift/Sema/ConstraintLocator.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
#ifndef SWIFT_SEMA_CONSTRAINTLOCATOR_H
1919
#define SWIFT_SEMA_CONSTRAINTLOCATOR_H
2020

21-
#include "swift/Basic/Debug.h"
22-
#include "swift/Basic/LLVM.h"
2321
#include "swift/AST/ASTNode.h"
2422
#include "swift/AST/Type.h"
2523
#include "swift/AST/Types.h"
24+
#include "swift/Basic/Debug.h"
25+
#include "swift/Basic/LLVM.h"
26+
#include "swift/Basic/NullablePtr.h"
2627
#include "llvm/ADT/ArrayRef.h"
2728
#include "llvm/ADT/FoldingSet.h"
2829
#include "llvm/ADT/PointerIntPair.h"
@@ -313,6 +314,10 @@ class ConstraintLocator : public llvm::FoldingSetNode {
313314
/// branch, and if so, the kind of branch.
314315
Optional<SingleValueStmtBranchKind> isForSingleValueStmtBranch() const;
315316

317+
/// If the locator in question is for a pattern match, returns the pattern,
318+
/// otherwise \c nullptr.
319+
NullablePtr<Pattern> getPatternMatch() const;
320+
316321
/// Returns true if \p locator is ending with either of the following
317322
/// - Member
318323
/// - Member -> KeyPathDynamicMember

lib/Sema/CSBindings.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2203,16 +2203,25 @@ TypeVariableBinding::fixForHole(ConstraintSystem &cs) const {
22032203
return std::make_pair(fix, /*impact=*/(unsigned)10);
22042204
}
22052205

2206-
if (auto pattern = getAsPattern(dstLocator->getAnchor())) {
2207-
if (dstLocator->getPath().size() == 1 &&
2208-
dstLocator->isLastElement<LocatorPathElt::PatternDecl>()) {
2206+
if (auto pattern = dstLocator->getPatternMatch()) {
2207+
if (dstLocator->isLastElement<LocatorPathElt::PatternDecl>()) {
2208+
// If this is the pattern in a for loop, and we have a mismatch of the
2209+
// element type, then we don't have any useful contextual information
2210+
// for the pattern, and can just bind to a hole without needing to penalize
2211+
// the solution further.
2212+
auto *seqLoc = cs.getConstraintLocator(
2213+
dstLocator->getAnchor(), ConstraintLocator::SequenceElementType);
2214+
if (cs.hasFixFor(seqLoc,
2215+
FixKind::IgnoreCollectionElementContextualMismatch)) {
2216+
return None;
2217+
}
22092218
// Not being able to infer the type of a variable in a pattern binding
22102219
// decl is more dramatic than anything that could happen inside the
22112220
// expression because we want to preferrably point the diagnostic to a
22122221
// part of the expression that caused us to be unable to infer the
22132222
// variable's type.
22142223
ConstraintFix *fix =
2215-
IgnoreUnresolvedPatternVar::create(cs, pattern, dstLocator);
2224+
IgnoreUnresolvedPatternVar::create(cs, pattern.get(), dstLocator);
22162225
return std::make_pair(fix, /*impact=*/(unsigned)100);
22172226
}
22182227
}

lib/Sema/CSGen.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2471,6 +2471,9 @@ namespace {
24712471
: nullptr;
24722472
};
24732473

2474+
auto matchLoc =
2475+
locator.withPathElement(LocatorPathElt::PatternMatch(pattern));
2476+
24742477
// Always prefer a contextual type when it's available.
24752478
if (externalPatternType) {
24762479
type = externalPatternType;
@@ -2479,8 +2482,8 @@ namespace {
24792482
type = CS.getType(initializer)->getRValueType();
24802483
} else {
24812484
type = CS.createTypeVariable(
2482-
CS.getConstraintLocator(pattern,
2483-
ConstraintLocator::AnyPatternDecl),
2485+
CS.getConstraintLocator(matchLoc,
2486+
LocatorPathElt::AnyPatternDecl()),
24842487
TVO_CanBindToNoEscape | TVO_CanBindToHole);
24852488
}
24862489
return setType(type);
@@ -2515,9 +2518,12 @@ namespace {
25152518
}
25162519
}
25172520

2521+
auto matchLoc =
2522+
locator.withPathElement(LocatorPathElt::PatternMatch(pattern));
2523+
25182524
if (!varType) {
25192525
varType = CS.createTypeVariable(
2520-
CS.getConstraintLocator(pattern,
2526+
CS.getConstraintLocator(matchLoc,
25212527
LocatorPathElt::NamedPatternDecl()),
25222528
TVO_CanBindToNoEscape | TVO_CanBindToHole);
25232529

lib/Sema/CSSimplify.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6228,16 +6228,6 @@ bool ConstraintSystem::repairFailures(
62286228
// `Int` vs. `(_, _)`.
62296229
recordAnyTypeVarAsPotentialHole(rhs);
62306230

6231-
// If the element type is `Any` i.e. `for (x, y) in [] { ... }`
6232-
// it would never match and the pattern (`rhs` = `(x, y)`)
6233-
// doesn't have any other source of contextual information,
6234-
// so instead of waiting for elements to become holes with an
6235-
// unrelated fixes, let's proactively bind all of the pattern
6236-
// elemnts to holes here.
6237-
if (lhs->isAny()) {
6238-
recordTypeVariablesAsHoles(rhs);
6239-
}
6240-
62416231
conversionsOrFixes.push_back(CollectionElementContextualMismatch::create(
62426232
*this, lhs, rhs, getConstraintLocator(locator)));
62436233
break;

lib/Sema/ConstraintLocator.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,14 @@ ConstraintLocator::isForSingleValueStmtBranch() const {
669669
return SingleValueStmtBranchKind::Regular;
670670
}
671671

672+
NullablePtr<Pattern> ConstraintLocator::getPatternMatch() const {
673+
auto matchElt = findLast<LocatorPathElt::PatternMatch>();
674+
if (!matchElt)
675+
return nullptr;
676+
677+
return matchElt->getPattern();
678+
}
679+
672680
bool ConstraintLocator::isMemberRef() const {
673681
if (isLastElement<LocatorPathElt::Member>()) {
674682
return true;

test/stmt/foreach.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,8 @@ do {
268268
for (x, y, z) in [] { // expected-error {{tuple pattern cannot match values of non-tuple type 'Any'}}
269269
}
270270
}
271+
272+
do {
273+
// https://github.com/apple/swift/issues/65650 - Make sure we say 'String', not 'Any'.
274+
for (x, y) in [""] {} // expected-error {{tuple pattern cannot match values of non-tuple type 'String'}}
275+
}

validation-test/Sema/type_checker_crashers_fixed/issue-65360.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,5 @@ let _: () -> Void = {
1616
let _: () -> Void = {
1717
for case (0)? in [a] {}
1818
for case (0, 0) in [a] {}
19-
// expected-error@-1 {{conflicting arguments to generic parameter 'Elements' ('[Any]' vs. '[Any?]')}}
20-
// expected-error@-2 {{conflicting arguments to generic parameter 'Element' ('Any' vs. 'Any?')}}
21-
// expected-error@-3 {{conflicting arguments to generic parameter 'Self' ('[Any]' vs. '[Any?]')}}
19+
// expected-error@-1 {{cannot convert value of type 'Any?' to expected element type '(_, _)'}}
2220
}

0 commit comments

Comments
 (0)