Skip to content

Commit dcf7dde

Browse files
committed
[Constraint system] Generate constraints for EnumElement patterns.
Generate a complete set of constraints for EnumElement patterns, e.g., case let .something(x, y) Most of the complication here comes from the implicit injection of optionals, e.g., this case can be matched to an optional of the enum type of which `something` is a member. To effect this change, introduce a locator for pattern matching and use it to permit implicit unwrapping during member lookup without triggering an error. Note also labels are dropped completely when performing the match, because labels can be added or removed when pattern matching. Label conflict are currently diagnosed as part of coercePatternToType, which suffices so long as overloading cases based on argument labels is not permitted. The primary observable change from this commit is in diagnostics: rather than diagnostics being triggered by `TypeChecker::coercePatternToType`, diagnostics for matching failures here go through the diagnostics machinery of the constraint solver. This is currently a regression, because there are no custom diagnostics for pattern match failures within the constraint system. This regression will be addressed in a subsequent commit; for now, leave those tests failing.
1 parent 08bfba7 commit dcf7dde

File tree

7 files changed

+171
-6
lines changed

7 files changed

+171
-6
lines changed

lib/Sema/CSGen.cpp

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2310,10 +2310,81 @@ namespace {
23102310
case PatternKind::Bool:
23112311
return setType(CS.getASTContext().getBoolDecl()->getDeclaredType());
23122312

2313+
case PatternKind::EnumElement: {
2314+
auto enumPattern = cast<EnumElementPattern>(pattern);
2315+
2316+
// Create a type variable to represent the pattern.
2317+
Type patternType =
2318+
CS.createTypeVariable(CS.getConstraintLocator(locator),
2319+
TVO_CanBindToNoEscape);
2320+
2321+
// Form the member constraint for a reference to a member of this
2322+
// type.
2323+
Type baseType;
2324+
Type memberType = CS.createTypeVariable(
2325+
CS.getConstraintLocator(locator),
2326+
TVO_CanBindToLValue | TVO_CanBindToNoEscape);
2327+
FunctionRefKind functionRefKind = FunctionRefKind::Compound;
2328+
if (!enumPattern->getParentType().isNull()) {
2329+
// Resolve the parent type.
2330+
Type parentType =
2331+
resolveTypeReferenceInExpression(enumPattern->getParentType());
2332+
parentType = CS.openUnboundGenericType(parentType, locator);
2333+
2334+
// Perform member lookup into the parent's metatype.
2335+
Type parentMetaType = MetatypeType::get(parentType);
2336+
CS.addValueMemberConstraint(
2337+
parentMetaType, enumPattern->getName(), memberType, CurDC,
2338+
functionRefKind, { },
2339+
locator.withPathElement(ConstraintLocator::PatternMatch));
2340+
2341+
// Parent type needs to be convertible to the pattern type; this
2342+
// accounts for cases where the pattern type is existential.
2343+
CS.addConstraint(ConstraintKind::Conversion, parentType, patternType,
2344+
locator);
2345+
2346+
baseType = parentType;
2347+
} else {
2348+
// Use the pattern type for member lookup.
2349+
CS.addUnresolvedValueMemberConstraint(
2350+
MetatypeType::get(patternType), enumPattern->getName(),
2351+
memberType, CurDC, functionRefKind,
2352+
locator.withPathElement(ConstraintLocator::PatternMatch));
2353+
2354+
baseType = patternType;
2355+
}
2356+
2357+
if (auto subPattern = enumPattern->getSubPattern()) {
2358+
// When there is a subpattern, the member will have function type,
2359+
// and we're matching the type of that subpattern to the parameter
2360+
// types.
2361+
Type subPatternType = getTypeForPattern(subPattern, locator);
2362+
SmallVector<AnyFunctionType::Param, 8> params;
2363+
AnyFunctionType::decomposeInput(subPatternType, params);
2364+
2365+
// Remove parameter labels; they aren't used when matching cases,
2366+
// but outright conflicts will be checked during coercion.
2367+
for (auto &param : params) {
2368+
param = param.getWithoutLabel();
2369+
}
2370+
2371+
Type outputType = CS.createTypeVariable(
2372+
CS.getConstraintLocator(locator),
2373+
TVO_CanBindToNoEscape);
2374+
Type functionType = FunctionType::get(params, outputType);
2375+
CS.addConstraint(ConstraintKind::Equal, functionType, memberType,
2376+
locator);
2377+
2378+
CS.addConstraint(ConstraintKind::Conversion, outputType, baseType,
2379+
locator);
2380+
}
2381+
2382+
return setType(patternType);
2383+
}
2384+
23132385
// Refutable patterns occur when checking the PatternBindingDecls in an
23142386
// if/let or while/let condition. They always require an initial value,
23152387
// so they always allow unspecified types.
2316-
case PatternKind::EnumElement:
23172388
case PatternKind::Expr:
23182389
// TODO: we could try harder here, e.g. for enum elements to provide the
23192390
// enum type.

lib/Sema/CSSimplify.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6039,6 +6039,24 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
60396039
}
60406040
}
60416041

6042+
// If we have candidates, and we're doing a member lookup for a pattern
6043+
// match, unwrap optionals and try again to allow implicit creation of
6044+
// optional "some" patterns (spelled "?").
6045+
if (result.ViableCandidates.empty() && result.UnviableCandidates.empty() &&
6046+
memberLocator->getLastElementAs<LocatorPathElt::PatternMatch>() &&
6047+
instanceTy->getOptionalObjectType() &&
6048+
baseObjTy->is<AnyMetatypeType>()) {
6049+
SmallVector<Type, 2> optionals;
6050+
Type instanceObjectTy = instanceTy->lookThroughAllOptionalTypes(optionals);
6051+
Type metaObjectType = MetatypeType::get(instanceObjectTy);
6052+
auto result = performMemberLookup(
6053+
constraintKind, memberName, metaObjectType,
6054+
functionRefKind, memberLocator, includeInaccessibleMembers);
6055+
result.numImplicitOptionalUnwraps = optionals.size();
6056+
result.actualBaseType = metaObjectType;
6057+
return result;
6058+
}
6059+
60426060
// If we have no viable or unviable candidates, and we're generating,
60436061
// diagnostics, rerun the query with inaccessible members included, so we can
60446062
// include them in the unviable candidates list.
@@ -6369,8 +6387,13 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
63696387
}
63706388

63716389
SmallVector<Constraint *, 4> candidates;
6390+
63726391
// If we found viable candidates, then we're done!
63736392
if (!result.ViableCandidates.empty()) {
6393+
// If we had to look in a different type, use that.
6394+
if (result.actualBaseType)
6395+
baseTy = result.actualBaseType;
6396+
63746397
// If only possible choice to refer to member is via keypath
63756398
// dynamic member dispatch, let's delay solving this constraint
63766399
// until constraint generation phase is complete, because

lib/Sema/ConstraintLocator.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
117117
case ConstraintLocator::DynamicCallable:
118118
case ConstraintLocator::ImplicitCallAsFunction:
119119
case ConstraintLocator::TernaryBranch:
120+
case ConstraintLocator::PatternMatch:
120121
return 0;
121122

122123
case ConstraintLocator::FunctionArgument:
@@ -476,12 +477,17 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const {
476477
out << "implicit reference to callAsFunction";
477478
break;
478479

479-
case TernaryBranch:
480+
case TernaryBranch: {
480481
auto branchElt = elt.castTo<LocatorPathElt::TernaryBranch>();
481482
out << (branchElt.forThen() ? "'then'" : "'else'")
482483
<< " branch of a ternary operator";
483484
break;
484485
}
486+
487+
case PatternMatch:
488+
out << "pattern match";
489+
break;
490+
}
485491
}
486492
out << ']';
487493
}

lib/Sema/ConstraintLocatorPathElts.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ SIMPLE_LOCATOR_PATH_ELT(DynamicCallable)
175175
/// The 'true' or 'false' branch of a ternary operator.
176176
CUSTOM_LOCATOR_PATH_ELT(TernaryBranch)
177177

178+
/// Performing a pattern patch.
179+
SIMPLE_LOCATOR_PATH_ELT(PatternMatch)
180+
178181
#undef LOCATOR_PATH_ELT
179182
#undef CUSTOM_LOCATOR_PATH_ELT
180183
#undef SIMPLE_LOCATOR_PATH_ELT

lib/Sema/ConstraintSystem.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,8 +1051,15 @@ struct MemberLookupResult {
10511051
/// If there is a favored candidate in the viable list, this indicates its
10521052
/// index.
10531053
unsigned FavoredChoice = ~0U;
1054-
1055-
1054+
1055+
/// The number of optional unwraps that were applied implicitly in the
1056+
/// lookup, for contexts where that is permitted.
1057+
unsigned numImplicitOptionalUnwraps = 0;
1058+
1059+
/// The base lookup type used to find the results, which will be non-null
1060+
/// only when it differs from the provided base type.
1061+
Type actualBaseType;
1062+
10561063
/// This enum tracks reasons why a candidate is not viable.
10571064
enum UnviableReason {
10581065
/// This uses a type like Self in its signature that cannot be used on an

test/Constraints/patterns.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ func rdar32241441() {
344344

345345

346346
// SR-6100
347-
struct One<Two> {
347+
struct One<Two> { // expected-note{{'Two' declared as parameter to type 'One'}}
348348
public enum E: Error {
349349
// if you remove associated value, everything works
350350
case SomeError(String)
@@ -354,7 +354,7 @@ struct One<Two> {
354354
func testOne() {
355355
do {
356356
} catch let error { // expected-warning{{'catch' block is unreachable because no errors are thrown in 'do' block}}
357-
if case One.E.SomeError = error {} // expected-error{{generic enum type 'One.E' is ambiguous without explicit generic parameters when matching value of type 'Error'}}
357+
if case One.E.SomeError = error {} // expected-error{{generic parameter 'Two' could not be inferred}}
358358
}
359359
}
360360

test/stmt/if_while_var.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,58 @@ func testWhileScoping(_ a: Int?) {// expected-note {{did you mean 'a'?}}
144144
useInt(x) // expected-error{{use of unresolved identifier 'x'}}
145145
}
146146

147+
// Matching a case with a single, labeled associated value.
148+
public enum SomeParseResult<T> {
149+
case error(length: Int)
150+
case repeated(value: String, repetitions: Int)
151+
152+
var _error: Int? {
153+
if case .error(let result) = self { return result }
154+
return nil
155+
}
156+
157+
var _error2: Int? {
158+
if case .error(length: let result) = self { return result }
159+
return nil
160+
}
161+
162+
var _error3: Int? {
163+
if case .error(wrong: let result) = self { return result } // expected-error{{tuple pattern element label 'wrong' must be 'length'}}
164+
return nil
165+
}
166+
167+
var _repeated: (String, Int)? {
168+
if case .repeated(let value, let repetitions) = self {
169+
return (value, repetitions)
170+
}
171+
return nil
172+
}
173+
174+
var _repeated2: (String, Int)? {
175+
if case .repeated(value: let value, let repetitions) = self {
176+
return (value, repetitions)
177+
}
178+
return nil
179+
}
180+
181+
var _repeated3: (String, Int)? {
182+
if case .repeated(let value, repetitions: let repetitions) = self {
183+
return (value, repetitions)
184+
}
185+
return nil
186+
}
187+
188+
var _repeated4: (String, Int)? {
189+
if case .repeated(value: let value, repetitions: let repetitions) = self {
190+
return (value, repetitions)
191+
}
192+
return nil
193+
}
194+
195+
var _repeated5: (String, Int)? {
196+
if case .repeated(value: let value, wrong: let repetitions) = self { // expected-error{{tuple pattern element label 'wrong' must be 'repetitions'}}
197+
return (value, repetitions)
198+
}
199+
return nil
200+
}
201+
}

0 commit comments

Comments
 (0)