Skip to content

Commit 44b91a6

Browse files
committed
Sema: Allow optional promotion of tuple patterns
1 parent 1b901a9 commit 44b91a6

File tree

11 files changed

+701
-4
lines changed

11 files changed

+701
-4
lines changed

include/swift/Sema/Constraint.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@ enum class ConstraintKind : char {
158158
/// The first type is an optional type whose object type is the second
159159
/// type, preserving lvalue-ness.
160160
OptionalObject,
161+
/// The first type is an optional of non-negative depth of the second type,
162+
/// ignoring lvalueness (because the constraint is currently applied only to
163+
/// pattern types, which are always rvalue). For example, given a RHS of
164+
/// 'Int', a LHS of 'Int' or 'Int??' would satisfy this constraint.
165+
EqualOrOptional,
161166
/// The first type is the same function type as the second type, but
162167
/// made @escaping.
163168
EscapableFunctionOf,
@@ -696,6 +701,7 @@ class Constraint final : public llvm::ilist_node<Constraint>,
696701
case ConstraintKind::DynamicCallableApplicableFunction:
697702
case ConstraintKind::BindOverload:
698703
case ConstraintKind::OptionalObject:
704+
case ConstraintKind::EqualOrOptional:
699705
case ConstraintKind::OneWayEqual:
700706
case ConstraintKind::OneWayBindParam:
701707
case ConstraintKind::DefaultClosureType:

include/swift/Sema/ConstraintSystem.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4693,6 +4693,12 @@ class ConstraintSystem {
46934693
TypeMatchOptions flags,
46944694
ConstraintLocatorBuilder locator);
46954695

4696+
/// Attempt to simplify an equal-or-optional constraint.
4697+
SolutionKind
4698+
simplifyEqualOrOptionalConstraint(Type first, Type second,
4699+
TypeMatchOptions flags,
4700+
ConstraintLocatorBuilder locator);
4701+
46964702
/// Attempt to simplify the BridgingConversion constraint.
46974703
SolutionKind simplifyBridgingConstraint(Type type1,
46984704
Type type2,

lib/Sema/CSBindings.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,6 +1440,7 @@ void PotentialBindings::infer(Constraint *constraint) {
14401440
case ConstraintKind::ArgumentConversion:
14411441
case ConstraintKind::OperatorArgumentConversion:
14421442
case ConstraintKind::OptionalObject:
1443+
case ConstraintKind::EqualOrOptional:
14431444
case ConstraintKind::UnresolvedMemberChainBase: {
14441445
auto binding = inferFromRelational(constraint);
14451446
if (!binding)

lib/Sema/CSGen.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2710,7 +2710,24 @@ namespace {
27102710
tupleTypeElts.push_back(TupleTypeElt(eltTy, tupleElt.getLabel()));
27112711
}
27122712

2713-
return setType(TupleType::get(tupleTypeElts, CS.getASTContext()));
2713+
Type patternTy = TupleType::get(tupleTypeElts, CS.getASTContext());
2714+
2715+
// 1. Allow optional promotion only when the matching is conditional,
2716+
// i.e., not in pattern binding declarations.
2717+
// 2. A single-element tuple can only mean that we are matching the
2718+
// payload of an enum case without splatting, in which case optional
2719+
// promotion is irrelevant.
2720+
if (!patternBinding && tupleTypeElts.size() > 1) {
2721+
Type tyVar = CS.createTypeVariable(CS.getConstraintLocator(locator),
2722+
TVO_CanBindToNoEscape);
2723+
CS.addConstraint(
2724+
ConstraintKind::EqualOrOptional, tyVar, patternTy,
2725+
locator.withPathElement(LocatorPathElt::PatternMatch(pattern)));
2726+
2727+
patternTy = tyVar;
2728+
}
2729+
2730+
return setType(patternTy);
27142731
}
27152732

27162733
case PatternKind::OptionalSome: {

lib/Sema/CSSimplify.cpp

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2244,6 +2244,7 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2,
22442244
case ConstraintKind::KeyPathApplication:
22452245
case ConstraintKind::LiteralConformsTo:
22462246
case ConstraintKind::OptionalObject:
2247+
case ConstraintKind::EqualOrOptional:
22472248
case ConstraintKind::SelfObjectOfProtocol:
22482249
case ConstraintKind::UnresolvedValueMember:
22492250
case ConstraintKind::ValueMember:
@@ -2619,6 +2620,7 @@ static bool matchFunctionRepresentations(FunctionType::ExtInfo einfo1,
26192620
case ConstraintKind::KeyPathApplication:
26202621
case ConstraintKind::LiteralConformsTo:
26212622
case ConstraintKind::OptionalObject:
2623+
case ConstraintKind::EqualOrOptional:
26222624
case ConstraintKind::SelfObjectOfProtocol:
26232625
case ConstraintKind::UnresolvedValueMember:
26242626
case ConstraintKind::ValueMember:
@@ -3126,6 +3128,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
31263128
case ConstraintKind::KeyPathApplication:
31273129
case ConstraintKind::LiteralConformsTo:
31283130
case ConstraintKind::OptionalObject:
3131+
case ConstraintKind::EqualOrOptional:
31293132
case ConstraintKind::SelfObjectOfProtocol:
31303133
case ConstraintKind::UnresolvedValueMember:
31313134
case ConstraintKind::ValueMember:
@@ -6732,6 +6735,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
67326735
case ConstraintKind::KeyPathApplication:
67336736
case ConstraintKind::LiteralConformsTo:
67346737
case ConstraintKind::OptionalObject:
6738+
case ConstraintKind::EqualOrOptional:
67356739
case ConstraintKind::SelfObjectOfProtocol:
67366740
case ConstraintKind::UnresolvedValueMember:
67376741
case ConstraintKind::ValueMember:
@@ -8873,6 +8877,60 @@ ConstraintSystem::simplifyOptionalObjectConstraint(
88738877
return SolutionKind::Solved;
88748878
}
88758879

8880+
ConstraintSystem::SolutionKind
8881+
ConstraintSystem::simplifyEqualOrOptionalConstraint(
8882+
Type first, Type second, TypeMatchOptions flags,
8883+
ConstraintLocatorBuilder locator) {
8884+
// Reify all the type variables we can.
8885+
first = getFixedTypeRecursive(first, flags, /*wantRValue=*/false);
8886+
second = getFixedTypeRecursive(second, flags, /*wantRValue=*/false);
8887+
8888+
assert(!first->hasLValueType() && !second->hasLValueType() &&
8889+
"Unexpected lvalue; constraint operand is not a pattern type?");
8890+
8891+
if (first->isEqual(second)) {
8892+
return SolutionKind::Solved;
8893+
}
8894+
8895+
// Unwrap both types simultaneously until one of them is no longer optional.
8896+
while (true) {
8897+
if (auto firstObject = first->getOptionalObjectType()) {
8898+
if (auto secondObject = second->getOptionalObjectType()) {
8899+
first = firstObject;
8900+
second = secondObject;
8901+
continue;
8902+
}
8903+
}
8904+
8905+
break;
8906+
}
8907+
8908+
const Type firstUnwrapped = first->lookThroughAllOptionalTypes();
8909+
8910+
// At this point at most one of the two types is a known optional. The
8911+
// constraint can be reduced to an equation between the fully unwrapped LHS
8912+
// and the RHS — and thus solved — if the following conditions are met:
8913+
// 1. The fully unwrapped LHS is not a type variable.
8914+
// 2. Either the LHS is not optional to begin with, or the RHS is not a
8915+
// type variable.
8916+
8917+
if (firstUnwrapped->isTypeVariableOrMember() ||
8918+
(first->isOptional() && second->isTypeVariableOrMember())) {
8919+
if (!flags.contains(TMF_GenerateConstraints)) {
8920+
return SolutionKind::Unsolved;
8921+
}
8922+
8923+
addUnsolvedConstraint(
8924+
Constraint::create(*this, ConstraintKind::EqualOrOptional, first,
8925+
second, getConstraintLocator(locator)));
8926+
return SolutionKind::Solved;
8927+
}
8928+
8929+
addConstraint(ConstraintKind::Bind, firstUnwrapped, second, locator);
8930+
8931+
return SolutionKind::Solved;
8932+
}
8933+
88768934
ConstraintSystem::SolutionKind
88778935
ConstraintSystem::simplifyBindTupleOfFunctionParamsConstraint(
88788936
Type first, Type second, TypeMatchOptions flags,
@@ -14528,6 +14586,9 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first,
1452814586
case ConstraintKind::OptionalObject:
1452914587
return simplifyOptionalObjectConstraint(first, second, subflags, locator);
1453014588

14589+
case ConstraintKind::EqualOrOptional:
14590+
return simplifyEqualOrOptionalConstraint(first, second, subflags, locator);
14591+
1453114592
case ConstraintKind::Defaultable:
1453214593
return simplifyDefaultableConstraint(first, second, subflags, locator);
1453314594

@@ -15074,7 +15135,12 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
1507415135
constraint.getSecondType(),
1507515136
/*flags*/ None,
1507615137
constraint.getLocator());
15077-
15138+
15139+
case ConstraintKind::EqualOrOptional:
15140+
return simplifyEqualOrOptionalConstraint(
15141+
constraint.getFirstType(), constraint.getSecondType(),
15142+
/*flags*/ None, constraint.getLocator());
15143+
1507815144
case ConstraintKind::ValueMember:
1507915145
case ConstraintKind::UnresolvedValueMember:
1508015146
return simplifyMemberConstraint(constraint.getKind(),

lib/Sema/CSSyntacticElement.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -624,8 +624,8 @@ class SyntacticElementConstraintGenerator
624624
return;
625625
}
626626

627-
// Convert the contextual type to the pattern, which establishes the
628-
// bindings.
627+
// Require a conversion from the contextual type to the pattern type, which
628+
// establishes the bindings.
629629
cs.addConstraint(ConstraintKind::Conversion, context.getType(), patternType,
630630
locator);
631631

lib/Sema/Constraint.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second,
7474
case ConstraintKind::EscapableFunctionOf:
7575
case ConstraintKind::OpenedExistentialOf:
7676
case ConstraintKind::OptionalObject:
77+
case ConstraintKind::EqualOrOptional:
7778
case ConstraintKind::OneWayEqual:
7879
case ConstraintKind::OneWayBindParam:
7980
case ConstraintKind::UnresolvedMemberChainBase:
@@ -152,6 +153,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, Type Third,
152153
case ConstraintKind::EscapableFunctionOf:
153154
case ConstraintKind::OpenedExistentialOf:
154155
case ConstraintKind::OptionalObject:
156+
case ConstraintKind::EqualOrOptional:
155157
case ConstraintKind::ApplicableFunction:
156158
case ConstraintKind::DynamicCallableApplicableFunction:
157159
case ConstraintKind::ValueMember:
@@ -309,6 +311,7 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const {
309311
case ConstraintKind::SelfObjectOfProtocol:
310312
case ConstraintKind::DynamicCallableApplicableFunction:
311313
case ConstraintKind::OptionalObject:
314+
case ConstraintKind::EqualOrOptional:
312315
case ConstraintKind::Defaultable:
313316
case ConstraintKind::OneWayEqual:
314317
case ConstraintKind::OneWayBindParam:
@@ -493,6 +496,9 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm,
493496
break;
494497
case ConstraintKind::OptionalObject:
495498
Out << " optional with object type "; break;
499+
case ConstraintKind::EqualOrOptional:
500+
Out << " equal to or optional of ";
501+
break;
496502
case ConstraintKind::BindOverload: {
497503
Out << " bound to ";
498504
auto overload = getOverloadChoice();
@@ -727,6 +733,7 @@ gatherReferencedTypeVars(Constraint *constraint,
727733
case ConstraintKind::EscapableFunctionOf:
728734
case ConstraintKind::OpenedExistentialOf:
729735
case ConstraintKind::OptionalObject:
736+
case ConstraintKind::EqualOrOptional:
730737
case ConstraintKind::Defaultable:
731738
case ConstraintKind::SubclassOf:
732739
case ConstraintKind::ConformsTo:

test/Constraints/patterns.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,3 +564,19 @@ struct TestIUOMatchOp {
564564
if case self = self {}
565565
}
566566
}
567+
568+
// Tuple patterns cannot match existentials without an explicit cast pattern.
569+
do {
570+
enum E {
571+
case e
572+
}
573+
574+
let a: Any
575+
let i: Int
576+
577+
// FIXME: Bad diagnostic when matching tuple pattern to existential (https://github.com/apple/swift/issues/65243)
578+
if case (i, i) = a {} // expected-error {{type of expression is ambiguous without more context}}
579+
if case (0, 0) = a {} // expected-error {{type of expression is ambiguous without more context}}
580+
581+
if case (E.e, E.e) = a {} // expected-error {{cannot convert value of type 'Any' to specified type '(E, E)'}}
582+
}

0 commit comments

Comments
 (0)