Skip to content

Commit f30202e

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 02aafea commit f30202e

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
@@ -2204,16 +2204,25 @@ TypeVariableBinding::fixForHole(ConstraintSystem &cs) const {
22042204
return std::make_pair(fix, /*impact=*/(unsigned)10);
22052205
}
22062206

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

lib/Sema/CSGen.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2522,6 +2522,9 @@ namespace {
25222522
: nullptr;
25232523
};
25242524

2525+
auto matchLoc =
2526+
locator.withPathElement(LocatorPathElt::PatternMatch(pattern));
2527+
25252528
// Always prefer a contextual type when it's available.
25262529
if (externalPatternType) {
25272530
type = externalPatternType;
@@ -2530,8 +2533,8 @@ namespace {
25302533
type = CS.getType(initializer)->getRValueType();
25312534
} else {
25322535
type = CS.createTypeVariable(
2533-
CS.getConstraintLocator(pattern,
2534-
ConstraintLocator::AnyPatternDecl),
2536+
CS.getConstraintLocator(matchLoc,
2537+
LocatorPathElt::AnyPatternDecl()),
25352538
TVO_CanBindToNoEscape | TVO_CanBindToHole);
25362539
}
25372540
return setType(type);
@@ -2566,9 +2569,12 @@ namespace {
25662569
}
25672570
}
25682571

2572+
auto matchLoc =
2573+
locator.withPathElement(LocatorPathElt::PatternMatch(pattern));
2574+
25692575
if (!varType) {
25702576
varType = CS.createTypeVariable(
2571-
CS.getConstraintLocator(pattern,
2577+
CS.getConstraintLocator(matchLoc,
25722578
LocatorPathElt::NamedPatternDecl()),
25732579
TVO_CanBindToNoEscape | TVO_CanBindToHole);
25742580

lib/Sema/CSSimplify.cpp

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

6248-
// If the element type is `Any` i.e. `for (x, y) in [] { ... }`
6249-
// it would never match and the pattern (`rhs` = `(x, y)`)
6250-
// doesn't have any other source of contextual information,
6251-
// so instead of waiting for elements to become holes with an
6252-
// unrelated fixes, let's proactively bind all of the pattern
6253-
// elemnts to holes here.
6254-
if (lhs->isAny()) {
6255-
recordTypeVariablesAsHoles(rhs);
6256-
}
6257-
62586248
conversionsOrFixes.push_back(CollectionElementContextualMismatch::create(
62596249
*this, lhs, rhs, getConstraintLocator(locator)));
62606250
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)