Skip to content

[Constraint system] Generate constraints from patterns #29879

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Feb 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
d2b2a50
[Constraint system] Record application targets for statement conditions.
DougGregor Feb 14, 2020
be8b9e5
[Constraint solver] Always produce optional types for '?' patterns.
DougGregor Feb 14, 2020
e1c0dec
Update some test cases for the OptionalSomePattern change.
DougGregor Feb 14, 2020
ee90374
[Constraint system] Finish an unfinished comment.
DougGregor Feb 23, 2020
232f20f
[Constraint solver] Generate constraints for “is” patterns.
DougGregor Feb 15, 2020
8fb05af
[Constraint solver] Give Bool patterns Bool type.
DougGregor Feb 15, 2020
270be17
[Type checker] Drop unused parameter from coercePatternToType().
DougGregor Feb 16, 2020
7bef540
[Constraint system] “is” pattern match should open the type
DougGregor Feb 23, 2020
244a823
[Constraint system] Maintain paren type sugar for patterns.
DougGregor Feb 23, 2020
bea9d06
[Constraint system] Set the type on all patterns, not just top-level …
DougGregor Feb 23, 2020
08bfba7
[Constraint system] Add conversion constraint for typed patterns.
DougGregor Feb 17, 2020
dcf7dde
[Constraint system] Generate constraints for EnumElement patterns.
DougGregor Feb 16, 2020
d607d3a
[Constraint system] Custom diagnostics for failed pattern matches.
DougGregor Feb 24, 2020
f6f80be
[Constraint system] Handle implicit tupling during pattern matching.
DougGregor Feb 24, 2020
6cfa0b0
[Constraint system] Don't dereference a null member locator.
DougGregor Feb 24, 2020
644ed76
[Constraint system] Prefer enum cases to static members when pattern …
DougGregor Feb 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2264,14 +2264,32 @@ bool ContextualFailure::diagnoseMissingFunctionCall() const {
return false;

auto *srcFT = getFromType()->getAs<FunctionType>();
if (!srcFT || !srcFT->getParams().empty())
if (!srcFT ||
!(srcFT->getParams().empty() ||
getLocator()->isLastElement<LocatorPathElt::PatternMatch>()))
return false;

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

// Diagnose cases where the pattern tried to match associated values but
// the case we found had none.
if (auto match =
getLocator()->getLastElementAs<LocatorPathElt::PatternMatch>()) {
if (auto enumElementPattern =
dyn_cast<EnumElementPattern>(match->getPattern())) {
emitDiagnostic(enumElementPattern->getNameLoc(),
diag::enum_element_pattern_assoc_values_mismatch,
enumElementPattern->getName());
emitDiagnostic(enumElementPattern->getNameLoc(),
diag::enum_element_pattern_assoc_values_remove)
.fixItRemove(enumElementPattern->getSubPattern()->getSourceRange());
return true;
}
}

auto *anchor = getAnchor();
emitDiagnostic(anchor->getLoc(), diag::missing_nullary_call,
srcFT->getResult())
Expand Down
172 changes: 147 additions & 25 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2198,18 +2198,38 @@ namespace {
///
/// \param pattern The pattern.
Type getTypeForPattern(Pattern *pattern, ConstraintLocatorBuilder locator) {
// If there's no pattern, then we have an unknown subpattern. Create a
// type variable.
if (!pattern) {
return CS.createTypeVariable(CS.getConstraintLocator(locator),
TVO_CanBindToNoEscape);
}

// Local function that must be called for each "return" throughout this
// function, to set the type of the pattern.
auto setType = [&](Type type) {
CS.setType(pattern, type);
return type;
};

switch (pattern->getKind()) {
case PatternKind::Paren:
// Parentheses don't affect the type.
return getTypeForPattern(cast<ParenPattern>(pattern)->getSubPattern(),
locator);
// Parentheses don't affect the canonical type, but record them as
// type sugar.
return setType(
ParenType::get(
CS.getASTContext(),
getTypeForPattern(
cast<ParenPattern>(pattern)->getSubPattern(), locator)));
case PatternKind::Var:
// Var doesn't affect the type.
return getTypeForPattern(cast<VarPattern>(pattern)->getSubPattern(),
locator);
return setType(
getTypeForPattern(
cast<VarPattern>(pattern)->getSubPattern(), locator));
case PatternKind::Any: {
return CS.createTypeVariable(CS.getConstraintLocator(locator),
TVO_CanBindToNoEscape);
return setType(
CS.createTypeVariable(CS.getConstraintLocator(locator),
TVO_CanBindToNoEscape));
}

case PatternKind::Named: {
Expand All @@ -2223,23 +2243,29 @@ namespace {
ROK = OA->get();
switch (optionalityOf(ROK)) {
case ReferenceOwnershipOptionality::Required:
return TypeChecker::getOptionalType(var->getLoc(), varType);
return setType(TypeChecker::getOptionalType(var->getLoc(), varType));
case ReferenceOwnershipOptionality::Allowed:
case ReferenceOwnershipOptionality::Disallowed:
return varType;
return setType(varType);
}
}

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

// For a typed pattern, simply return the opened type of the pattern.
// FIXME: Error recovery if the type is an error type?
return openedType;
// Determine the subpattern type. It will be convertible to the
// ascribed type.
Type subPatternType =
getTypeForPattern(
cast<TypedPattern>(pattern)->getSubPattern(), locator);
CS.addConstraint(
ConstraintKind::Conversion, subPatternType, openedType, locator);
return setType(openedType);
}

case PatternKind::Tuple: {
Expand All @@ -2253,19 +2279,119 @@ namespace {
LocatorPathElt::TupleElement(i)));
tupleTypeElts.push_back(TupleTypeElt(eltTy, tupleElt.getLabel()));
}
return TupleType::get(tupleTypeElts, CS.getASTContext());
return setType(TupleType::get(tupleTypeElts, CS.getASTContext()));
}


case PatternKind::OptionalSome: {
// The subpattern must have optional type.
Type subPatternType = getTypeForPattern(
cast<OptionalSomePattern>(pattern)->getSubPattern(), locator);

return setType(OptionalType::get(subPatternType));
}

case PatternKind::Is: {
auto isPattern = cast<IsPattern>(pattern);
Type subPatternType =
getTypeForPattern(isPattern->getSubPattern(), locator);

// Make sure we can cast from the subpattern type to the type we're
// checking; if it's impossible, fail.
if (Type castType =
resolveTypeReferenceInExpression(isPattern->getCastTypeLoc())) {
castType = CS.openUnboundGenericType(castType, locator);
CS.addConstraint(
ConstraintKind::CheckedCast, subPatternType, castType, locator);
}

return setType(subPatternType);
}

case PatternKind::Bool:
return setType(CS.getASTContext().getBoolDecl()->getDeclaredType());

case PatternKind::EnumElement: {
auto enumPattern = cast<EnumElementPattern>(pattern);

// Create a type variable to represent the pattern.
Type patternType =
CS.createTypeVariable(CS.getConstraintLocator(locator),
TVO_CanBindToNoEscape);

// Form the member constraint for a reference to a member of this
// type.
Type baseType;
Type memberType = CS.createTypeVariable(
CS.getConstraintLocator(locator),
TVO_CanBindToLValue | TVO_CanBindToNoEscape);
FunctionRefKind functionRefKind = FunctionRefKind::Compound;
if (!enumPattern->getParentType().isNull()) {
// Resolve the parent type.
Type parentType =
resolveTypeReferenceInExpression(enumPattern->getParentType());
parentType = CS.openUnboundGenericType(parentType, locator);

// Perform member lookup into the parent's metatype.
Type parentMetaType = MetatypeType::get(parentType);
CS.addValueMemberConstraint(
parentMetaType, enumPattern->getName(), memberType, CurDC,
functionRefKind, { },
locator.withPathElement(LocatorPathElt::PatternMatch(pattern)));

// Parent type needs to be convertible to the pattern type; this
// accounts for cases where the pattern type is existential.
CS.addConstraint(ConstraintKind::Conversion, parentType, patternType,
locator);

baseType = parentType;
} else {
// Use the pattern type for member lookup.
CS.addUnresolvedValueMemberConstraint(
MetatypeType::get(patternType), enumPattern->getName(),
memberType, CurDC, functionRefKind,
locator.withPathElement(LocatorPathElt::PatternMatch(pattern)));

baseType = patternType;
}

if (auto subPattern = enumPattern->getSubPattern()) {
// When there is a subpattern, the member will have function type,
// and we're matching the type of that subpattern to the parameter
// types.
Type subPatternType = getTypeForPattern(subPattern, locator);
SmallVector<AnyFunctionType::Param, 8> params;
AnyFunctionType::decomposeInput(subPatternType, params);

// Remove parameter labels; they aren't used when matching cases,
// but outright conflicts will be checked during coercion.
for (auto &param : params) {
param = param.getWithoutLabel();
}

Type outputType = CS.createTypeVariable(
CS.getConstraintLocator(locator),
TVO_CanBindToNoEscape);
Type functionType = FunctionType::get(params, outputType);
CS.addConstraint(
ConstraintKind::Equal, functionType, memberType,
locator.withPathElement(LocatorPathElt::PatternMatch(pattern)));

CS.addConstraint(ConstraintKind::Conversion, outputType, baseType,
locator);
}

return setType(patternType);
}

// Refutable patterns occur when checking the PatternBindingDecls in an
// if/let or while/let condition. They always require an initial value,
// so they always allow unspecified types.
#define PATTERN(Id, Parent)
#define REFUTABLE_PATTERN(Id, Parent) case PatternKind::Id:
#include "swift/AST/PatternNodes.def"
case PatternKind::Expr:
// TODO: we could try harder here, e.g. for enum elements to provide the
// enum type.
return CS.createTypeVariable(CS.getConstraintLocator(locator),
TVO_CanBindToNoEscape);
return setType(
CS.createTypeVariable(
CS.getConstraintLocator(locator), TVO_CanBindToNoEscape));
}

llvm_unreachable("Unhandled pattern kind");
Expand Down Expand Up @@ -3845,11 +3971,7 @@ static bool generateInitPatternConstraints(
auto locator =
cs.getConstraintLocator(initializer, LocatorPathElt::ContextualType());
Type patternType = cs.generateConstraints(pattern, locator);
if (!patternType)
return true;

// Record the type of this pattern.
cs.setType(pattern, patternType);
assert(patternType && "All patterns have a type");

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

/// Create a fresh type variable to describe the type of the
/// Create a fresh type variable to describe the type of the bound variable.
Type varType = createTypeVariable(locator, TVO_CanBindToNoEscape);

auto ROK = ReferenceOwnership::Strong;
Expand Down
69 changes: 69 additions & 0 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1658,6 +1658,12 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
implodeParams(func2Params);
}
}
} else if (last->getKind() == ConstraintLocator::PatternMatch &&
isa<EnumElementPattern>(
last->castTo<LocatorPathElt::PatternMatch>().getPattern()) &&
isSingleTupleParam(ctx, func1Params) &&
canImplodeParams(func2Params)) {
implodeParams(func2Params);
}
}

Expand Down Expand Up @@ -3920,6 +3926,25 @@ bool ConstraintSystem::repairFailures(
break;
}

case ConstraintLocator::PatternMatch: {
// If either type is a hole, consider this fixed.
if (lhs->isHole() || rhs->isHole())
return true;

// If the left-hand side is a function type and the pattern is an enum
// element pattern, call it a contextual mismatch.
auto pattern = elt.castTo<LocatorPathElt::PatternMatch>().getPattern();
if (lhs->is<FunctionType>() && isa<EnumElementPattern>(pattern)) {
markAnyTypeVarsAsPotentialHoles(lhs);
markAnyTypeVarsAsPotentialHoles(rhs);

conversionsOrFixes.push_back(ContextualMismatch::create(
*this, lhs, rhs, getConstraintLocator(locator)));
}

break;
}

default:
break;
}
Expand Down Expand Up @@ -5668,6 +5693,22 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
}
}

// If we are pattern-matching an enum element and we found any enum elements,
// ignore anything that isn't an enum element.
bool onlyAcceptEnumElements = false;
if (memberLocator &&
memberLocator->isLastElement<LocatorPathElt::PatternMatch>() &&
isa<EnumElementPattern>(
memberLocator->getLastElementAs<LocatorPathElt::PatternMatch>()
->getPattern())) {
for (const auto &result: lookup) {
if (isa<EnumElementDecl>(result.getValueDecl())) {
onlyAcceptEnumElements = true;
break;
}
}
}

// If the instance type is String bridged to NSString, compute
// the type we'll look in for bridging.
Type bridgedType;
Expand All @@ -5692,6 +5733,10 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
return;
}

// If we only accept enum elements but this isn't one, ignore it.
if (onlyAcceptEnumElements && !isa<EnumElementDecl>(decl))
return;

// Dig out the instance type and figure out what members of the instance type
// we are going to see.
auto baseTy = candidate.getBaseType();
Expand Down Expand Up @@ -6039,6 +6084,25 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
}
}

// If we have candidates, and we're doing a member lookup for a pattern
// match, unwrap optionals and try again to allow implicit creation of
// optional "some" patterns (spelled "?").
if (result.ViableCandidates.empty() && result.UnviableCandidates.empty() &&
memberLocator &&
memberLocator->isLastElement<LocatorPathElt::PatternMatch>() &&
instanceTy->getOptionalObjectType() &&
baseObjTy->is<AnyMetatypeType>()) {
SmallVector<Type, 2> optionals;
Type instanceObjectTy = instanceTy->lookThroughAllOptionalTypes(optionals);
Type metaObjectType = MetatypeType::get(instanceObjectTy);
auto result = performMemberLookup(
constraintKind, memberName, metaObjectType,
functionRefKind, memberLocator, includeInaccessibleMembers);
result.numImplicitOptionalUnwraps = optionals.size();
result.actualBaseType = metaObjectType;
return result;
}

// If we have no viable or unviable candidates, and we're generating,
// diagnostics, rerun the query with inaccessible members included, so we can
// include them in the unviable candidates list.
Expand Down Expand Up @@ -6369,8 +6433,13 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
}

SmallVector<Constraint *, 4> candidates;

// If we found viable candidates, then we're done!
if (!result.ViableCandidates.empty()) {
// If we had to look in a different type, use that.
if (result.actualBaseType)
baseTy = result.actualBaseType;

// If only possible choice to refer to member is via keypath
// dynamic member dispatch, let's delay solving this constraint
// until constraint generation phase is complete, because
Expand Down
Loading