Skip to content

Commit fe5c068

Browse files
authored
Merge pull request #29879 from DougGregor/constraint-system-patterns
[Constraint system] Generate constraints from patterns
2 parents 31d63a3 + 644ed76 commit fe5c068

16 files changed

+433
-54
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2264,14 +2264,32 @@ bool ContextualFailure::diagnoseMissingFunctionCall() const {
22642264
return false;
22652265

22662266
auto *srcFT = getFromType()->getAs<FunctionType>();
2267-
if (!srcFT || !srcFT->getParams().empty())
2267+
if (!srcFT ||
2268+
!(srcFT->getParams().empty() ||
2269+
getLocator()->isLastElement<LocatorPathElt::PatternMatch>()))
22682270
return false;
22692271

22702272
auto toType = getToType();
22712273
if (toType->is<AnyFunctionType>() ||
22722274
!TypeChecker::isConvertibleTo(srcFT->getResult(), toType, getDC()))
22732275
return false;
22742276

2277+
// Diagnose cases where the pattern tried to match associated values but
2278+
// the case we found had none.
2279+
if (auto match =
2280+
getLocator()->getLastElementAs<LocatorPathElt::PatternMatch>()) {
2281+
if (auto enumElementPattern =
2282+
dyn_cast<EnumElementPattern>(match->getPattern())) {
2283+
emitDiagnostic(enumElementPattern->getNameLoc(),
2284+
diag::enum_element_pattern_assoc_values_mismatch,
2285+
enumElementPattern->getName());
2286+
emitDiagnostic(enumElementPattern->getNameLoc(),
2287+
diag::enum_element_pattern_assoc_values_remove)
2288+
.fixItRemove(enumElementPattern->getSubPattern()->getSourceRange());
2289+
return true;
2290+
}
2291+
}
2292+
22752293
auto *anchor = getAnchor();
22762294
emitDiagnostic(anchor->getLoc(), diag::missing_nullary_call,
22772295
srcFT->getResult())

lib/Sema/CSGen.cpp

Lines changed: 147 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2198,18 +2198,38 @@ namespace {
21982198
///
21992199
/// \param pattern The pattern.
22002200
Type getTypeForPattern(Pattern *pattern, ConstraintLocatorBuilder locator) {
2201+
// If there's no pattern, then we have an unknown subpattern. Create a
2202+
// type variable.
2203+
if (!pattern) {
2204+
return CS.createTypeVariable(CS.getConstraintLocator(locator),
2205+
TVO_CanBindToNoEscape);
2206+
}
2207+
2208+
// Local function that must be called for each "return" throughout this
2209+
// function, to set the type of the pattern.
2210+
auto setType = [&](Type type) {
2211+
CS.setType(pattern, type);
2212+
return type;
2213+
};
2214+
22012215
switch (pattern->getKind()) {
22022216
case PatternKind::Paren:
2203-
// Parentheses don't affect the type.
2204-
return getTypeForPattern(cast<ParenPattern>(pattern)->getSubPattern(),
2205-
locator);
2217+
// Parentheses don't affect the canonical type, but record them as
2218+
// type sugar.
2219+
return setType(
2220+
ParenType::get(
2221+
CS.getASTContext(),
2222+
getTypeForPattern(
2223+
cast<ParenPattern>(pattern)->getSubPattern(), locator)));
22062224
case PatternKind::Var:
22072225
// Var doesn't affect the type.
2208-
return getTypeForPattern(cast<VarPattern>(pattern)->getSubPattern(),
2209-
locator);
2226+
return setType(
2227+
getTypeForPattern(
2228+
cast<VarPattern>(pattern)->getSubPattern(), locator));
22102229
case PatternKind::Any: {
2211-
return CS.createTypeVariable(CS.getConstraintLocator(locator),
2212-
TVO_CanBindToNoEscape);
2230+
return setType(
2231+
CS.createTypeVariable(CS.getConstraintLocator(locator),
2232+
TVO_CanBindToNoEscape));
22132233
}
22142234

22152235
case PatternKind::Named: {
@@ -2223,23 +2243,29 @@ namespace {
22232243
ROK = OA->get();
22242244
switch (optionalityOf(ROK)) {
22252245
case ReferenceOwnershipOptionality::Required:
2226-
return TypeChecker::getOptionalType(var->getLoc(), varType);
2246+
return setType(TypeChecker::getOptionalType(var->getLoc(), varType));
22272247
case ReferenceOwnershipOptionality::Allowed:
22282248
case ReferenceOwnershipOptionality::Disallowed:
2229-
return varType;
2249+
return setType(varType);
22302250
}
22312251
}
22322252

22332253
case PatternKind::Typed: {
22342254
// FIXME: Need a better locator for a pattern as a base.
2255+
// Compute the type ascribed to the pattern.
22352256
auto contextualPattern =
22362257
ContextualPattern::forRawPattern(pattern, CurDC);
22372258
Type type = TypeChecker::typeCheckPattern(contextualPattern);
22382259
Type openedType = CS.openUnboundGenericType(type, locator);
22392260

2240-
// For a typed pattern, simply return the opened type of the pattern.
2241-
// FIXME: Error recovery if the type is an error type?
2242-
return openedType;
2261+
// Determine the subpattern type. It will be convertible to the
2262+
// ascribed type.
2263+
Type subPatternType =
2264+
getTypeForPattern(
2265+
cast<TypedPattern>(pattern)->getSubPattern(), locator);
2266+
CS.addConstraint(
2267+
ConstraintKind::Conversion, subPatternType, openedType, locator);
2268+
return setType(openedType);
22432269
}
22442270

22452271
case PatternKind::Tuple: {
@@ -2253,19 +2279,119 @@ namespace {
22532279
LocatorPathElt::TupleElement(i)));
22542280
tupleTypeElts.push_back(TupleTypeElt(eltTy, tupleElt.getLabel()));
22552281
}
2256-
return TupleType::get(tupleTypeElts, CS.getASTContext());
2282+
return setType(TupleType::get(tupleTypeElts, CS.getASTContext()));
22572283
}
2258-
2284+
2285+
case PatternKind::OptionalSome: {
2286+
// The subpattern must have optional type.
2287+
Type subPatternType = getTypeForPattern(
2288+
cast<OptionalSomePattern>(pattern)->getSubPattern(), locator);
2289+
2290+
return setType(OptionalType::get(subPatternType));
2291+
}
2292+
2293+
case PatternKind::Is: {
2294+
auto isPattern = cast<IsPattern>(pattern);
2295+
Type subPatternType =
2296+
getTypeForPattern(isPattern->getSubPattern(), locator);
2297+
2298+
// Make sure we can cast from the subpattern type to the type we're
2299+
// checking; if it's impossible, fail.
2300+
if (Type castType =
2301+
resolveTypeReferenceInExpression(isPattern->getCastTypeLoc())) {
2302+
castType = CS.openUnboundGenericType(castType, locator);
2303+
CS.addConstraint(
2304+
ConstraintKind::CheckedCast, subPatternType, castType, locator);
2305+
}
2306+
2307+
return setType(subPatternType);
2308+
}
2309+
2310+
case PatternKind::Bool:
2311+
return setType(CS.getASTContext().getBoolDecl()->getDeclaredType());
2312+
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(LocatorPathElt::PatternMatch(pattern)));
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(LocatorPathElt::PatternMatch(pattern)));
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(
2376+
ConstraintKind::Equal, functionType, memberType,
2377+
locator.withPathElement(LocatorPathElt::PatternMatch(pattern)));
2378+
2379+
CS.addConstraint(ConstraintKind::Conversion, outputType, baseType,
2380+
locator);
2381+
}
2382+
2383+
return setType(patternType);
2384+
}
2385+
22592386
// Refutable patterns occur when checking the PatternBindingDecls in an
22602387
// if/let or while/let condition. They always require an initial value,
22612388
// so they always allow unspecified types.
2262-
#define PATTERN(Id, Parent)
2263-
#define REFUTABLE_PATTERN(Id, Parent) case PatternKind::Id:
2264-
#include "swift/AST/PatternNodes.def"
2389+
case PatternKind::Expr:
22652390
// TODO: we could try harder here, e.g. for enum elements to provide the
22662391
// enum type.
2267-
return CS.createTypeVariable(CS.getConstraintLocator(locator),
2268-
TVO_CanBindToNoEscape);
2392+
return setType(
2393+
CS.createTypeVariable(
2394+
CS.getConstraintLocator(locator), TVO_CanBindToNoEscape));
22692395
}
22702396

22712397
llvm_unreachable("Unhandled pattern kind");
@@ -3845,11 +3971,7 @@ static bool generateInitPatternConstraints(
38453971
auto locator =
38463972
cs.getConstraintLocator(initializer, LocatorPathElt::ContextualType());
38473973
Type patternType = cs.generateConstraints(pattern, locator);
3848-
if (!patternType)
3849-
return true;
3850-
3851-
// Record the type of this pattern.
3852-
cs.setType(pattern, patternType);
3974+
assert(patternType && "All patterns have a type");
38533975

38543976
if (auto wrappedVar = target.getInitializationWrappedVar()) {
38553977
// Add an equal constraint between the pattern type and the
@@ -4035,7 +4157,7 @@ void ConstraintSystem::bindVariablesInPattern(
40354157
case PatternKind::Named: {
40364158
auto var = cast<NamedPattern>(pattern)->getDecl();
40374159

4038-
/// Create a fresh type variable to describe the type of the
4160+
/// Create a fresh type variable to describe the type of the bound variable.
40394161
Type varType = createTypeVariable(locator, TVO_CanBindToNoEscape);
40404162

40414163
auto ROK = ReferenceOwnership::Strong;

lib/Sema/CSSimplify.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,6 +1658,12 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
16581658
implodeParams(func2Params);
16591659
}
16601660
}
1661+
} else if (last->getKind() == ConstraintLocator::PatternMatch &&
1662+
isa<EnumElementPattern>(
1663+
last->castTo<LocatorPathElt::PatternMatch>().getPattern()) &&
1664+
isSingleTupleParam(ctx, func1Params) &&
1665+
canImplodeParams(func2Params)) {
1666+
implodeParams(func2Params);
16611667
}
16621668
}
16631669

@@ -3920,6 +3926,25 @@ bool ConstraintSystem::repairFailures(
39203926
break;
39213927
}
39223928

3929+
case ConstraintLocator::PatternMatch: {
3930+
// If either type is a hole, consider this fixed.
3931+
if (lhs->isHole() || rhs->isHole())
3932+
return true;
3933+
3934+
// If the left-hand side is a function type and the pattern is an enum
3935+
// element pattern, call it a contextual mismatch.
3936+
auto pattern = elt.castTo<LocatorPathElt::PatternMatch>().getPattern();
3937+
if (lhs->is<FunctionType>() && isa<EnumElementPattern>(pattern)) {
3938+
markAnyTypeVarsAsPotentialHoles(lhs);
3939+
markAnyTypeVarsAsPotentialHoles(rhs);
3940+
3941+
conversionsOrFixes.push_back(ContextualMismatch::create(
3942+
*this, lhs, rhs, getConstraintLocator(locator)));
3943+
}
3944+
3945+
break;
3946+
}
3947+
39233948
default:
39243949
break;
39253950
}
@@ -5668,6 +5693,22 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
56685693
}
56695694
}
56705695

5696+
// If we are pattern-matching an enum element and we found any enum elements,
5697+
// ignore anything that isn't an enum element.
5698+
bool onlyAcceptEnumElements = false;
5699+
if (memberLocator &&
5700+
memberLocator->isLastElement<LocatorPathElt::PatternMatch>() &&
5701+
isa<EnumElementPattern>(
5702+
memberLocator->getLastElementAs<LocatorPathElt::PatternMatch>()
5703+
->getPattern())) {
5704+
for (const auto &result: lookup) {
5705+
if (isa<EnumElementDecl>(result.getValueDecl())) {
5706+
onlyAcceptEnumElements = true;
5707+
break;
5708+
}
5709+
}
5710+
}
5711+
56715712
// If the instance type is String bridged to NSString, compute
56725713
// the type we'll look in for bridging.
56735714
Type bridgedType;
@@ -5692,6 +5733,10 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
56925733
return;
56935734
}
56945735

5736+
// If we only accept enum elements but this isn't one, ignore it.
5737+
if (onlyAcceptEnumElements && !isa<EnumElementDecl>(decl))
5738+
return;
5739+
56955740
// Dig out the instance type and figure out what members of the instance type
56965741
// we are going to see.
56975742
auto baseTy = candidate.getBaseType();
@@ -6039,6 +6084,25 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
60396084
}
60406085
}
60416086

6087+
// If we have candidates, and we're doing a member lookup for a pattern
6088+
// match, unwrap optionals and try again to allow implicit creation of
6089+
// optional "some" patterns (spelled "?").
6090+
if (result.ViableCandidates.empty() && result.UnviableCandidates.empty() &&
6091+
memberLocator &&
6092+
memberLocator->isLastElement<LocatorPathElt::PatternMatch>() &&
6093+
instanceTy->getOptionalObjectType() &&
6094+
baseObjTy->is<AnyMetatypeType>()) {
6095+
SmallVector<Type, 2> optionals;
6096+
Type instanceObjectTy = instanceTy->lookThroughAllOptionalTypes(optionals);
6097+
Type metaObjectType = MetatypeType::get(instanceObjectTy);
6098+
auto result = performMemberLookup(
6099+
constraintKind, memberName, metaObjectType,
6100+
functionRefKind, memberLocator, includeInaccessibleMembers);
6101+
result.numImplicitOptionalUnwraps = optionals.size();
6102+
result.actualBaseType = metaObjectType;
6103+
return result;
6104+
}
6105+
60426106
// If we have no viable or unviable candidates, and we're generating,
60436107
// diagnostics, rerun the query with inaccessible members included, so we can
60446108
// include them in the unviable candidates list.
@@ -6369,8 +6433,13 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
63696433
}
63706434

63716435
SmallVector<Constraint *, 4> candidates;
6436+
63726437
// If we found viable candidates, then we're done!
63736438
if (!result.ViableCandidates.empty()) {
6439+
// If we had to look in a different type, use that.
6440+
if (result.actualBaseType)
6441+
baseTy = result.actualBaseType;
6442+
63746443
// If only possible choice to refer to member is via keypath
63756444
// dynamic member dispatch, let's delay solving this constraint
63766445
// until constraint generation phase is complete, because

0 commit comments

Comments
 (0)