mirror of
https://github.com/apple/swift.git
synced 2026-02-27 18:26:24 +01:00
987 lines
37 KiB
C++
987 lines
37 KiB
C++
//===--- ConstraintSystem.cpp - Constraint-based Type Checking ------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2026 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements various subclasses of BindingProducer to generate
|
|
// successive choices in a binding set, disjunction, or conjunction.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "swift/Sema/BindingProducer.h"
|
|
#include "swift/Sema/Constraint.h"
|
|
#include "swift/Sema/ConstraintSystem.h"
|
|
#include "swift/Sema/TypeVariableType.h"
|
|
|
|
using namespace swift;
|
|
using namespace constraints;
|
|
using namespace inference;
|
|
|
|
// Given a possibly-Optional type, return the direct superclass of the
|
|
// (underlying) type wrapped in the same number of optional levels as
|
|
// type.
|
|
static Type getOptionalSuperclass(Type type) {
|
|
int optionalLevels = 0;
|
|
while (auto underlying = type->getOptionalObjectType()) {
|
|
++optionalLevels;
|
|
type = underlying;
|
|
}
|
|
|
|
Type superclass;
|
|
if (auto *existential = type->getAs<ExistentialType>()) {
|
|
auto constraintTy = existential->getConstraintType();
|
|
if (auto *compositionTy = constraintTy->getAs<ProtocolCompositionType>()) {
|
|
SmallVector<Type, 2> members;
|
|
bool found = false;
|
|
// Preserve all of the protocol requirements of the type i.e.
|
|
// if the type was `any B & P` where `B : A` the supertype is
|
|
// going to be `any A & P`.
|
|
//
|
|
// This is especially important for Sendable key paths because
|
|
// to reserve sendability of the original type.
|
|
for (auto member : compositionTy->getMembers()) {
|
|
if (member->getClassOrBoundGenericClass()) {
|
|
member = member->getSuperclass();
|
|
if (!member)
|
|
return Type();
|
|
found = true;
|
|
}
|
|
members.push_back(member);
|
|
}
|
|
|
|
if (!found)
|
|
return Type();
|
|
|
|
superclass = ExistentialType::get(
|
|
ProtocolCompositionType::get(type->getASTContext(), members,
|
|
compositionTy->getInverses(),
|
|
compositionTy->hasExplicitAnyObject()));
|
|
} else {
|
|
// Avoid producing superclass for situations like `any P` where `P` is
|
|
// `protocol P : C`.
|
|
return Type();
|
|
}
|
|
} else {
|
|
superclass = type->getSuperclass();
|
|
}
|
|
|
|
if (!superclass)
|
|
return Type();
|
|
|
|
while (optionalLevels--)
|
|
superclass = OptionalType::get(superclass);
|
|
|
|
return superclass;
|
|
}
|
|
|
|
/// Enumerates all of the 'direct' supertypes of the given type.
|
|
///
|
|
/// The direct supertype S of a type T is a supertype of T (e.g., T < S)
|
|
/// such that there is no type U where T < U and U < S.
|
|
static SmallVector<Type, 4> enumerateDirectSupertypes(Type type) {
|
|
SmallVector<Type, 4> result;
|
|
|
|
if (type->is<InOutType>() || type->is<LValueType>()) {
|
|
type = type->getWithoutSpecifierType();
|
|
result.push_back(type);
|
|
}
|
|
|
|
if (auto superclass = getOptionalSuperclass(type)) {
|
|
// FIXME: Can also weaken to the set of protocol constraints, but only
|
|
// if there are any protocols that the type conforms to but the superclass
|
|
// does not.
|
|
|
|
result.push_back(superclass);
|
|
}
|
|
|
|
// FIXME: lots of other cases to consider!
|
|
return result;
|
|
}
|
|
|
|
TypeVarBindingProducer::TypeVarBindingProducer(
|
|
ConstraintSystem &cs,
|
|
TypeVariableType *typeVar,
|
|
const BindingSet &bindings)
|
|
: BindingProducer(cs, typeVar->getImpl().getLocator()),
|
|
TypeVar(typeVar), CanBeNil(bindings.canBeNil()) {
|
|
if (bindings.isDirectHole()) {
|
|
auto *locator = getLocator();
|
|
// If this type variable is associated with a code completion token
|
|
// and it failed to infer any bindings let's adjust holes's locator
|
|
// to point to a code completion token to avoid attempting to "fix"
|
|
// this problem since its rooted in the fact that constraint system
|
|
// is under-constrained.
|
|
if (bindings.getAssociatedCodeCompletionToken()) {
|
|
locator =
|
|
CS.getConstraintLocator(bindings.getAssociatedCodeCompletionToken());
|
|
}
|
|
|
|
Bindings.push_back(Binding::forHole(TypeVar, locator));
|
|
return;
|
|
}
|
|
|
|
// A binding to `Any` which should always be considered as a last resort.
|
|
std::optional<Binding> Any;
|
|
|
|
auto addBinding = [&](const Binding &binding) {
|
|
// Adjust optionality of existing bindings based on presence of
|
|
// `ExpressibleByNilLiteral` requirement.
|
|
if (requiresOptionalAdjustment(binding)) {
|
|
Bindings.push_back(
|
|
binding.withType(OptionalType::get(binding.BindingType)));
|
|
} else if (binding.BindingType->isAny()) {
|
|
Any.emplace(binding);
|
|
} else {
|
|
Bindings.push_back(binding);
|
|
}
|
|
};
|
|
|
|
if (TypeVar->getImpl().isPackExpansion()) {
|
|
SmallVector<Binding> viableBindings;
|
|
|
|
// Collect possible contextual types (keep in mind that pack
|
|
// expansion type variable gets bound to its "opened" type
|
|
// regardless). To be viable the binding has to come from `bind`
|
|
// or `equal` constraint (i.e. same-type constraint or explicit
|
|
// generic argument) and be fully resolved.
|
|
llvm::copy_if(bindings.Bindings, std::back_inserter(viableBindings),
|
|
[&](const Binding &binding) {
|
|
auto *source = binding.getSource();
|
|
if (source->getKind() == ConstraintKind::Bind ||
|
|
source->getKind() == ConstraintKind::Equal) {
|
|
auto type = binding.BindingType;
|
|
return type->is<PackExpansionType>() &&
|
|
!type->hasTypeVariable();
|
|
}
|
|
return false;
|
|
});
|
|
|
|
// If there is a single fully resolved contextual type, let's
|
|
// use it as a binding to help with performance and diagnostics.
|
|
if (viableBindings.size() == 1) {
|
|
addBinding(viableBindings.front());
|
|
} else {
|
|
for (auto *constraint : bindings.Defaults) {
|
|
Bindings.push_back(getDefaultBinding(constraint));
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
for (const auto &binding : bindings.Bindings) {
|
|
addBinding(binding);
|
|
}
|
|
|
|
// Infer defaults based on "uncovered" literal protocol requirements.
|
|
for (const auto &literal : bindings.Literals) {
|
|
if (!literal.viableAsBinding())
|
|
continue;
|
|
|
|
// We need to figure out whether this is a direct conformance
|
|
// requirement or inferred transitive one to identify binding
|
|
// kind correctly.
|
|
addBinding({literal.getDefaultType(),
|
|
literal.isDirectRequirement() ? BindingKind::Subtypes
|
|
: BindingKind::Supertypes,
|
|
literal.getSource()});
|
|
}
|
|
|
|
// Let's always consider `Any` to be a last resort binding because
|
|
// it's always better to infer concrete type and erase it if required
|
|
// by the context.
|
|
if (Any) {
|
|
Bindings.push_back(*Any);
|
|
}
|
|
|
|
{
|
|
bool noBindings = Bindings.empty();
|
|
|
|
for (auto *constraint : bindings.Defaults) {
|
|
if (noBindings) {
|
|
// If there are no direct or transitive bindings to attempt
|
|
// let's add defaults to the list right away.
|
|
Bindings.push_back(getDefaultBinding(constraint));
|
|
} else {
|
|
// Otherwise let's delay attempting default bindings
|
|
// until all of the direct & transitive bindings and
|
|
// their derivatives have been attempted.
|
|
DelayedDefaults.push_back(constraint);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool TypeVarBindingProducer::requiresOptionalAdjustment(
|
|
const Binding &binding) const {
|
|
// If type variable can't be `nil` then adjustment is
|
|
// not required.
|
|
if (!CanBeNil)
|
|
return false;
|
|
|
|
if (binding.Kind == BindingKind::Supertypes) {
|
|
auto type = binding.BindingType->getRValueType();
|
|
// If the type doesn't conform to ExpressibleByNilLiteral,
|
|
// produce an optional of that type as a potential binding. We
|
|
// overwrite the binding in place because the non-optional type
|
|
// will fail to type-check against the nil-literal conformance.
|
|
auto *proto = CS.getASTContext().getProtocol(
|
|
KnownProtocolKind::ExpressibleByNilLiteral);
|
|
|
|
return !CS.lookupConformance(type, proto);
|
|
} else if (binding.isDefaultableBinding() && binding.BindingType->isAny()) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
PotentialBinding
|
|
TypeVarBindingProducer::getDefaultBinding(Constraint *constraint) const {
|
|
assert(constraint->getKind() == ConstraintKind::Defaultable ||
|
|
constraint->getKind() == ConstraintKind::FallbackType);
|
|
|
|
auto type = constraint->getSecondType();
|
|
Binding binding{type, BindingKind::Exact, constraint};
|
|
return requiresOptionalAdjustment(binding)
|
|
? binding.withType(OptionalType::get(type))
|
|
: binding;
|
|
}
|
|
|
|
bool TypeVarBindingProducer::computeNext() {
|
|
SmallVector<Binding, 4> newBindings;
|
|
auto addNewBinding = [&](Binding binding) {
|
|
auto type = binding.BindingType;
|
|
|
|
// If we've already tried this binding, move on.
|
|
if (!BoundTypes.insert(type.getPointer()).second)
|
|
return;
|
|
|
|
if (!ExploredTypes.insert(type->getCanonicalType()).second)
|
|
return;
|
|
|
|
newBindings.push_back(std::move(binding));
|
|
};
|
|
|
|
// Let's attempt only directly inferrable bindings for
|
|
// a type variable representing a closure type because
|
|
// such type variables are handled specially and only
|
|
// bound to a type inferred from their expression, having
|
|
// contextual bindings is just a trigger for that to
|
|
// happen.
|
|
if (TypeVar->getImpl().isClosureType())
|
|
return false;
|
|
|
|
for (auto &binding : Bindings) {
|
|
const auto type = binding.BindingType;
|
|
assert(!type->hasError());
|
|
|
|
// If we have a protocol with a default type, look for alternative
|
|
// types to the default.
|
|
if (NumTries == 0 && binding.hasDefaultedLiteralProtocol()) {
|
|
auto knownKind =
|
|
*(binding.getDefaultedLiteralProtocol()->getKnownProtocolKind());
|
|
SmallVector<Type, 2> scratch;
|
|
for (auto altType : CS.getAlternativeLiteralTypes(knownKind, scratch)) {
|
|
addNewBinding(binding.withSameSource(altType, BindingKind::Subtypes));
|
|
}
|
|
}
|
|
|
|
if (getLocator()->directlyAt<ForceValueExpr>() &&
|
|
TypeVar->getImpl().canBindToLValue() &&
|
|
!binding.BindingType->is<LValueType>()) {
|
|
// Result of force unwrap is always connected to its base
|
|
// optional type via `OptionalObject` constraint which
|
|
// preserves l-valueness, so in case where object type got
|
|
// inferred before optional type (because it got the
|
|
// type from context e.g. parameter type of a function call),
|
|
// we need to test type with and without l-value after
|
|
// delaying bindings for as long as possible.
|
|
addNewBinding(binding.withType(LValueType::get(binding.BindingType)));
|
|
}
|
|
|
|
// There is a tailored fix for optional key path root references,
|
|
// let's not create ambiguity by attempting unwrap when it's
|
|
// not allowed.
|
|
if (binding.Kind != BindingKind::Subtypes &&
|
|
getLocator()->isKeyPathRoot() && type->getOptionalObjectType())
|
|
continue;
|
|
|
|
// Allow solving for T even for a binding kind where that's invalid
|
|
// if fixes are allowed, because that gives us the opportunity to
|
|
// match T? values to the T binding by adding an unwrap fix.
|
|
if (binding.Kind == BindingKind::Subtypes || CS.shouldAttemptFixes()) {
|
|
// If we were unsuccessful solving for T?, try solving for T.
|
|
if (auto objTy = type->getOptionalObjectType()) {
|
|
// TODO: This could be generalized in the future to cover all patterns
|
|
// that have an intermediate type variable in subtype/conversion chain.
|
|
//
|
|
// Let's not perform $T? -> $T for closure result types to avoid having
|
|
// to re-discover solutions that differ only in location of optional
|
|
// injection. `Void` is a special case because in $T_result position
|
|
// it has special semantics and enables T? -> Void conversions.
|
|
//
|
|
// The pattern with such type variables is:
|
|
//
|
|
// $T_body <conv/subtype> $T_result <conv/subtype> $T_contextual_result
|
|
//
|
|
// When $T_contextual_result is Optional<$U>, the optional injection
|
|
// can either happen from $T_body or from $T_result (if `return`
|
|
// expression is non-optional), if we allow both the solver would
|
|
// find two solutions that differ only in location of optional
|
|
// injection.
|
|
if (!TypeVar->getImpl().isClosureResultType() || objTy->isVoid() ||
|
|
objTy->isTypeVariableOrMember()) {
|
|
// If T is a type variable, only attempt this if both the
|
|
// type variable we are trying bindings for, and the type
|
|
// variable we will attempt to bind, both have the same
|
|
// polarity with respect to being able to bind lvalues.
|
|
if (auto otherTypeVar = objTy->getAs<TypeVariableType>()) {
|
|
if (TypeVar->getImpl().canBindToLValue() ==
|
|
otherTypeVar->getImpl().canBindToLValue()) {
|
|
addNewBinding(binding.withType(objTy));
|
|
}
|
|
} else {
|
|
addNewBinding(binding.withType(objTy));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
auto srcLocator = binding.getLocator();
|
|
if (srcLocator &&
|
|
(srcLocator->isLastElement<LocatorPathElt::ApplyArgToParam>() ||
|
|
srcLocator->isLastElement<LocatorPathElt::AutoclosureResult>()) &&
|
|
!type->hasTypeVariable() && type->isKnownStdlibCollectionType()) {
|
|
// If the type binding comes from the argument conversion, let's
|
|
// instead of binding collection types directly, try to bind
|
|
// using temporary type variables substituted for element
|
|
// types, that's going to ensure that subtype relationship is
|
|
// always preserved.
|
|
auto *BGT = type->castTo<BoundGenericType>();
|
|
auto dstLocator = TypeVar->getImpl().getLocator();
|
|
auto newType =
|
|
CS.openUnboundGenericType(BGT->getDecl(), BGT->getParent(),
|
|
dstLocator, /*isTypeResolution=*/false)
|
|
->reconstituteSugar(/*recursive=*/false);
|
|
addNewBinding(binding.withType(newType));
|
|
}
|
|
|
|
if (binding.Kind == BindingKind::Supertypes) {
|
|
// If this is a type variable representing closure result,
|
|
// which is on the right-side of some relational constraint
|
|
// let's have it try `Void` as well because there is an
|
|
// implicit conversion `() -> T` to `() -> Void` and this
|
|
// helps to avoid creating a thunk to support it.
|
|
// Avoid doing this is we already have a hole binding since
|
|
// introducing Void will just cause local solution ambiguities.
|
|
if (getLocator()->isLastElement<LocatorPathElt::ClosureResult>() &&
|
|
binding.Kind == AllowedBindingKind::Supertypes &&
|
|
!binding.BindingType->isPlaceholder()) {
|
|
auto voidType = CS.getASTContext().TheEmptyTupleType;
|
|
addNewBinding(binding.withSameSource(voidType, BindingKind::Exact));
|
|
}
|
|
|
|
for (auto supertype : enumerateDirectSupertypes(type)) {
|
|
// If we're not allowed to try this binding, skip it.
|
|
if (checkTypeOfBinding(TypeVar, supertype)) {
|
|
// A key path type cannot be bound to type-erased key path variants.
|
|
if (TypeVar->getImpl().isKeyPathType() &&
|
|
isTypeErasedKeyPathType(supertype))
|
|
continue;
|
|
|
|
addNewBinding(binding.withType(supertype));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newBindings.empty()) {
|
|
// If key path type had contextual types, let's not attempt fallback.
|
|
if (TypeVar->getImpl().isKeyPathType() && !ExploredTypes.empty())
|
|
return false;
|
|
|
|
// Add defaultable constraints (if any).
|
|
for (auto *constraint : DelayedDefaults) {
|
|
if (constraint->getKind() == ConstraintKind::FallbackType) {
|
|
// If there are no other possible bindings for this variable
|
|
// let's default it to the fallback type, otherwise we should
|
|
// only attempt contextual types.
|
|
if (!ExploredTypes.empty())
|
|
continue;
|
|
}
|
|
|
|
addNewBinding(getDefaultBinding(constraint));
|
|
}
|
|
|
|
// Drop all of the default since we have converted them into bindings.
|
|
DelayedDefaults.clear();
|
|
}
|
|
|
|
if (newBindings.empty())
|
|
return false;
|
|
|
|
Index = 0;
|
|
++NumTries;
|
|
Bindings = std::move(newBindings);
|
|
return true;
|
|
}
|
|
|
|
std::optional<std::pair<ConstraintFix *, unsigned>>
|
|
TypeVariableBinding::fixForHole(ConstraintSystem &cs) const {
|
|
auto *dstLocator = TypeVar->getImpl().getLocator();
|
|
auto *srcLocator = Binding.getLocator();
|
|
|
|
// FIXME: This check could be turned into an assert once
|
|
// all code completion kinds are ported to use
|
|
// `TypeChecker::typeCheckForCodeCompletion` API.
|
|
if (cs.isForCodeCompletion()) {
|
|
// If the hole is originated from code completion expression
|
|
// let's not try to fix this, anything connected to a
|
|
// code completion is allowed to be a hole because presence
|
|
// of a code completion token makes constraint system
|
|
// under-constrained due to e.g. lack of expressions on the
|
|
// right-hand side of the token, which are required for a
|
|
// regular type-check.
|
|
if (dstLocator->directlyAt<CodeCompletionExpr>() ||
|
|
srcLocator->directlyAt<CodeCompletionExpr>())
|
|
return std::nullopt;
|
|
}
|
|
|
|
unsigned defaultImpact = 1;
|
|
|
|
if (auto *GP = TypeVar->getImpl().getGenericParameter()) {
|
|
// If it is representative for a key path root, let's emit a more
|
|
// specific diagnostic.
|
|
auto *keyPathRoot =
|
|
cs.isRepresentativeFor(TypeVar, ConstraintLocator::KeyPathRoot);
|
|
if (keyPathRoot) {
|
|
ConstraintFix *fix = SpecifyKeyPathRootType::create(
|
|
cs, keyPathRoot->getImpl().getLocator());
|
|
return std::make_pair(fix, defaultImpact);
|
|
} else {
|
|
auto path = dstLocator->getPath();
|
|
// Drop `generic parameter` locator element so that all missing
|
|
// generic parameters related to the same path can be coalesced later.
|
|
ConstraintFix *fix = DefaultGenericArgument::create(
|
|
cs, GP,
|
|
cs.getConstraintLocator(dstLocator->getAnchor(), path.drop_back()));
|
|
return std::make_pair(fix, defaultImpact);
|
|
}
|
|
}
|
|
|
|
if (TypeVar->getImpl().isClosureParameterType()) {
|
|
ConstraintFix *fix = SpecifyClosureParameterType::create(cs, dstLocator);
|
|
return std::make_pair(fix, defaultImpact);
|
|
}
|
|
|
|
if (TypeVar->getImpl().isClosureResultType()) {
|
|
auto *closure = castToExpr<ClosureExpr>(dstLocator->getAnchor());
|
|
// If the whole body is being ignored due to a pre-check failure,
|
|
// let's not record a fix about result type since there is
|
|
// just not enough context to infer it without a body.
|
|
auto *closureLoc = cs.getConstraintLocator(closure);
|
|
if (cs.hasFixFor(closureLoc, FixKind::IgnoreInvalidResultBuilderBody) ||
|
|
cs.hasFixFor(closureLoc, FixKind::IgnoreResultBuilderWithReturnStmts))
|
|
return std::nullopt;
|
|
|
|
ConstraintFix *fix = SpecifyClosureReturnType::create(cs, dstLocator);
|
|
return std::make_pair(fix, defaultImpact);
|
|
}
|
|
|
|
if (srcLocator->directlyAt<ObjectLiteralExpr>()) {
|
|
ConstraintFix *fix = SpecifyObjectLiteralTypeImport::create(cs, dstLocator);
|
|
return std::make_pair(fix, defaultImpact);
|
|
}
|
|
|
|
if (srcLocator->isKeyPathRoot()) {
|
|
// If we recorded an invalid key path fix, let's skip this specify root
|
|
// type fix because it wouldn't produce a useful diagnostic.
|
|
auto *kpLocator = cs.getConstraintLocator(srcLocator->getAnchor());
|
|
if (cs.hasFixFor(kpLocator, FixKind::AllowKeyPathWithoutComponents))
|
|
return std::nullopt;
|
|
|
|
// If key path has any invalid component, let's just skip fix because the
|
|
// invalid component would be already diagnosed.
|
|
auto keyPath = castToExpr<KeyPathExpr>(srcLocator->getAnchor());
|
|
if (llvm::any_of(keyPath->getComponents(),
|
|
[](KeyPathExpr::Component component) {
|
|
return !component.isValid();
|
|
}))
|
|
return std::nullopt;
|
|
|
|
ConstraintFix *fix = SpecifyKeyPathRootType::create(cs, dstLocator);
|
|
return std::make_pair(fix, defaultImpact);
|
|
}
|
|
|
|
if (srcLocator->isLastElement<LocatorPathElt::PlaceholderType>()) {
|
|
// When a 'nil' has a placeholder as contextual type there is not enough
|
|
// information to resolve it, so let's record a specify contextual type for
|
|
// nil fix.
|
|
if (isExpr<NilLiteralExpr>(srcLocator->getAnchor())) {
|
|
ConstraintFix *fix = SpecifyContextualTypeForNil::create(cs, dstLocator);
|
|
return std::make_pair(fix, /*impact=*/(unsigned)10);
|
|
}
|
|
|
|
// If the placeholder is in an invalid position, we'll have already
|
|
// recorded a fix, and can skip recording another.
|
|
if (cs.hasFixFor(dstLocator, FixKind::IgnoreInvalidPlaceholder))
|
|
return std::nullopt;
|
|
|
|
ConstraintFix *fix = SpecifyTypeForPlaceholder::create(cs, srcLocator);
|
|
return std::make_pair(fix, defaultImpact);
|
|
}
|
|
|
|
if (dstLocator->directlyAt<NilLiteralExpr>()) {
|
|
// This is a dramatic event, it means that there is absolutely
|
|
// no contextual information to resolve type of `nil`.
|
|
ConstraintFix *fix = SpecifyContextualTypeForNil::create(cs, dstLocator);
|
|
return std::make_pair(fix, /*impact=*/(unsigned)10);
|
|
}
|
|
|
|
if (auto pattern = dstLocator->getPatternMatch()) {
|
|
if (dstLocator->isLastElement<LocatorPathElt::PatternDecl>()) {
|
|
// If this is the pattern in a for loop, and we have a mismatch of the
|
|
// element type, then we don't have any useful contextual information
|
|
// for the pattern, and can just bind to a hole without needing to penalize
|
|
// the solution further.
|
|
auto *seqLoc = cs.getConstraintLocator(
|
|
dstLocator->getAnchor(), ConstraintLocator::SequenceElementType);
|
|
if (cs.hasFixFor(seqLoc,
|
|
FixKind::IgnoreCollectionElementContextualMismatch)) {
|
|
return std::nullopt;
|
|
}
|
|
if (dstLocator->getAnchor().isExpr(ExprKind::CodeCompletion)) {
|
|
// Ignore the hole if it is because the right-hand-side of the pattern
|
|
// match is a code completion token. Assigning a high fix score to this
|
|
// mismatch won't help. In fact, it can harm because we might have a
|
|
// different exploration path in the constraint system that gives up
|
|
// earlier (eg. because code completion is in a closure that doesn't
|
|
// match the expected parameter of a function call) and might thus get a
|
|
// better score, despite not having any information about the code
|
|
// completion token at all.
|
|
return std::nullopt;
|
|
}
|
|
// Not being able to infer the type of a variable in a pattern binding
|
|
// decl is more dramatic than anything that could happen inside the
|
|
// expression because we want to preferrably point the diagnostic to a
|
|
// part of the expression that caused us to be unable to infer the
|
|
// variable's type.
|
|
ConstraintFix *fix =
|
|
IgnoreUnresolvedPatternVar::create(cs, pattern.get(), dstLocator);
|
|
return std::make_pair(fix, /*impact=*/(unsigned)100);
|
|
}
|
|
}
|
|
|
|
if (srcLocator->isLastElement<LocatorPathElt::MemberRefBase>()) {
|
|
auto *baseExpr = castToExpr<UnresolvedMemberExpr>(srcLocator->getAnchor());
|
|
ConstraintFix *fix = SpecifyBaseTypeForContextualMember::create(
|
|
cs, baseExpr->getName(), srcLocator);
|
|
return std::make_pair(fix, defaultImpact);
|
|
}
|
|
|
|
if (dstLocator->isLastElement<LocatorPathElt::PackElement>()) {
|
|
// A hole appears as an element of generic pack params
|
|
ConstraintFix *Fix = SpecifyPackElementType::create(cs, dstLocator);
|
|
return std::make_pair(Fix, defaultImpact);
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
static bool shouldIgnoreHoleForCodeCompletion(ConstraintSystem &cs,
|
|
TypeVariableType *typeVar,
|
|
ConstraintLocator *srcLocator) {
|
|
if (!cs.isForCodeCompletion())
|
|
return false;
|
|
|
|
// Don't penalize solutions with unresolved generics.
|
|
if (typeVar->getImpl().getGenericParameter())
|
|
return true;
|
|
|
|
// Don't penalize solutions if we couldn't determine the type of the code
|
|
// completion token. We still want to examine the surrounding types in
|
|
// that case.
|
|
if (typeVar->getImpl().isCodeCompletionToken())
|
|
return true;
|
|
|
|
// When doing completion in a result builder, we avoid solving unrelated
|
|
// expressions by replacing them with unbound placeholder variables.
|
|
// As such, we need to avoid penalizing holes for references to
|
|
// placeholder variables.
|
|
if (srcLocator->isLastElement<LocatorPathElt::PlaceholderType>()) {
|
|
if (auto *DRE = getAsExpr<DeclRefExpr>(srcLocator->getAnchor())) {
|
|
if (auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl())) {
|
|
if (auto *PBD = VD->getParentPatternBinding()) {
|
|
if (isPlaceholderVar(PBD))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Don't penalize solutions with holes due to missing arguments after the
|
|
// code completion position.
|
|
auto argLoc = srcLocator->findLast<LocatorPathElt::SynthesizedArgument>();
|
|
if (argLoc && argLoc->isAfterCodeCompletionLoc())
|
|
return true;
|
|
|
|
// Don't penalize solutions that have holes for ignored arguments.
|
|
if (cs.hasArgumentsIgnoredForCodeCompletion()) {
|
|
// Avoid simplifying the locator if the constraint system didn't ignore
|
|
// any arguments.
|
|
auto argExpr = simplifyLocatorToAnchor(typeVar->getImpl().getLocator());
|
|
if (cs.isArgumentIgnoredForCodeCompletion(argExpr.dyn_cast<Expr *>())) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
|
|
auto type = Binding.BindingType;
|
|
auto *srcLocator = Binding.getLocator();
|
|
auto *dstLocator = TypeVar->getImpl().getLocator();
|
|
|
|
if (Binding.hasDefaultedLiteralProtocol()) {
|
|
type = cs.replaceInferableTypesWithTypeVars(type, dstLocator);
|
|
type = type->reconstituteSugar(/*recursive=*/false);
|
|
}
|
|
|
|
// If type variable has been marked as a possible hole due to
|
|
// e.g. reference to a missing member. Let's propagate that
|
|
// information to the object type of the optional type it's
|
|
// about to be bound to.
|
|
//
|
|
// In some situations like pattern bindings e.g. `if let x = base?.member`
|
|
// - if `member` doesn't exist, `x` cannot be determined either, which
|
|
// leaves `OptionalEvaluationExpr` representing outer type of `base?.member`
|
|
// without any contextual information, so even though `x` would get
|
|
// bound to result type of the chain, underlying type variable wouldn't
|
|
// be resolved, so we need to propagate holes up the conversion chain.
|
|
// Also propagate in code completion mode because in some cases code
|
|
// completion relies on type variable being a potential hole.
|
|
if (TypeVar->getImpl().canBindToHole()) {
|
|
if (srcLocator->directlyAt<OptionalEvaluationExpr>() ||
|
|
cs.isForCodeCompletion()) {
|
|
if (auto objectTy = type->getOptionalObjectType()) {
|
|
if (auto *typeVar = objectTy->getAs<TypeVariableType>()) {
|
|
cs.recordPotentialHole(typeVar);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ConstraintSystem::TypeMatchOptions options;
|
|
|
|
options |= ConstraintSystem::TMF_GenerateConstraints;
|
|
options |= ConstraintSystem::TMF_BindingTypeVariable;
|
|
|
|
auto result =
|
|
cs.matchTypes(TypeVar, type, ConstraintKind::Bind, options, srcLocator);
|
|
|
|
if (result.isFailure()) {
|
|
if (cs.isDebugMode()) {
|
|
PrintOptions PO = PrintOptions::forDebugging();
|
|
|
|
llvm::errs().indent(cs.solverState->getCurrentIndent())
|
|
<< "(failed to establish binding " << TypeVar->getString(PO)
|
|
<< " := " << type->getString(PO) << ")\n";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
auto reportHole = [&]() {
|
|
if (shouldIgnoreHoleForCodeCompletion(cs, TypeVar, srcLocator))
|
|
return false;
|
|
|
|
// Reflect in the score that this type variable couldn't be
|
|
// resolved and had to be bound to a placeholder "hole" type.
|
|
cs.increaseScore(SK_Hole, srcLocator);
|
|
|
|
if (auto fix = fixForHole(cs)) {
|
|
if (cs.recordFix(/*fix=*/fix->first, /*impact=*/fix->second))
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
// If this was from a defaultable binding note that.
|
|
if (Binding.isDefaultableBinding()) {
|
|
cs.recordDefaultedConstraint(srcLocator);
|
|
|
|
// Fail if hole reporting fails.
|
|
if (type->isPlaceholder() && reportHole())
|
|
return false;
|
|
}
|
|
|
|
if (cs.simplify())
|
|
return false;
|
|
|
|
// If all of the re-activated constraints where simplified,
|
|
// let's notify binding inference about the fact that type
|
|
// variable has been bound successfully.
|
|
cs.getConstraintGraph().introduceToInference(TypeVar, type);
|
|
|
|
return true;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// Find any type variable references inside of an AST node.
|
|
class TypeVariableRefFinder : public ASTWalker {
|
|
/// A stack of all closures the walker encountered so far.
|
|
SmallVector<DeclContext *> ClosureDCs;
|
|
|
|
ConstraintSystem &CS;
|
|
ASTNode Parent;
|
|
|
|
llvm::SmallPtrSetImpl<TypeVariableType *> &ReferencedVars;
|
|
|
|
public:
|
|
TypeVariableRefFinder(
|
|
ConstraintSystem &cs, ASTNode parent, ContextualTypeInfo context,
|
|
llvm::SmallPtrSetImpl<TypeVariableType *> &referencedVars)
|
|
: CS(cs), Parent(parent), ReferencedVars(referencedVars) {
|
|
if (auto ty = context.getType())
|
|
inferVariables(ty);
|
|
if (auto *closure = getAsExpr<ClosureExpr>(Parent))
|
|
ClosureDCs.push_back(closure);
|
|
}
|
|
|
|
MacroWalking getMacroWalkingBehavior() const override {
|
|
return MacroWalking::Arguments;
|
|
}
|
|
|
|
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
|
|
if (auto *closure = dyn_cast<ClosureExpr>(expr)) {
|
|
ClosureDCs.push_back(closure);
|
|
}
|
|
|
|
if (auto *joinExpr = dyn_cast<TypeJoinExpr>(expr)) {
|
|
// If this join is over a known type, let's
|
|
// analyze it too because it can contain type
|
|
// variables.
|
|
if (!joinExpr->getVar())
|
|
inferVariables(joinExpr->getType());
|
|
}
|
|
|
|
if (auto *DRE = dyn_cast<DeclRefExpr>(expr)) {
|
|
auto *decl = DRE->getDecl();
|
|
|
|
if (auto type = CS.getTypeIfAvailable(decl)) {
|
|
auto &ctx = CS.getASTContext();
|
|
// If this is not one of the closure parameters which
|
|
// is inferrable from the body, let's replace type
|
|
// variables with errors to avoid bringing external
|
|
// information to the element component.
|
|
if (type->hasTypeVariable() &&
|
|
!(isa<ParamDecl>(decl) || decl->getName() == ctx.Id_builderSelf)) {
|
|
// If there are type variables left in the simplified version,
|
|
// it means that this is an invalid external declaration
|
|
// relative to this element's context.
|
|
if (CS.simplifyType(type)->hasTypeVariable()) {
|
|
auto transformedTy = type.transformRec([&](Type type) -> std::optional<Type> {
|
|
if (type->is<TypeVariableType>()) {
|
|
return Type(ErrorType::get(CS.getASTContext()));
|
|
}
|
|
return std::nullopt;
|
|
});
|
|
|
|
CS.setType(decl, transformedTy);
|
|
return Action::Continue(expr);
|
|
}
|
|
}
|
|
|
|
inferVariables(type);
|
|
return Action::Continue(expr);
|
|
}
|
|
|
|
auto var = dyn_cast<VarDecl>(decl);
|
|
if (!var)
|
|
return Action::Continue(expr);
|
|
|
|
// If there is no type recorded yet, let's check whether
|
|
// it is a placeholder variable implicitly generated by the
|
|
// compiler.
|
|
if (auto *PB = var->getParentPatternBinding()) {
|
|
if (auto placeholderTy = isPlaceholderVar(PB)) {
|
|
auto openedTy = CS.replaceInferableTypesWithTypeVars(
|
|
placeholderTy, CS.getConstraintLocator(expr));
|
|
inferVariables(openedTy);
|
|
CS.setType(var, openedTy);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If closure appears inside of a pack expansion, the elements
|
|
// that reference pack elements have to bring expansion's shape
|
|
// type in scope to make sure that the shapes match.
|
|
if (auto *packElement = getAsExpr<PackElementExpr>(expr)) {
|
|
if (auto *outerExpansion = CS.getPackElementExpansion(packElement)) {
|
|
auto *expansionTy = CS.simplifyType(CS.getType(outerExpansion))
|
|
->castTo<PackExpansionType>();
|
|
expansionTy->getCountType()->getTypeVariables(ReferencedVars);
|
|
}
|
|
}
|
|
|
|
return Action::Continue(expr);
|
|
}
|
|
|
|
PostWalkResult<Expr *> walkToExprPost(Expr *expr) override {
|
|
if (isa<ClosureExpr>(expr)) {
|
|
ClosureDCs.pop_back();
|
|
}
|
|
return Action::Continue(expr);
|
|
}
|
|
|
|
PreWalkResult<Stmt *> walkToStmtPre(Stmt *stmt) override {
|
|
// Return statements have to reference outside result type
|
|
// since all of them are joined by it if it's not specified
|
|
// explicitly.
|
|
if (isa<ReturnStmt>(stmt)) {
|
|
if (auto *closure = getAsExpr<ClosureExpr>(Parent)) {
|
|
// Return is only viable if it belongs to a parent closure.
|
|
if (currentClosureDC() == closure)
|
|
inferVariables(CS.getClosureType(closure)->getResult());
|
|
}
|
|
}
|
|
|
|
return Action::Continue(stmt);
|
|
}
|
|
|
|
PreWalkAction walkToDeclPre(Decl *D) override {
|
|
/// Decls get type-checked separately, except for PatternBindingDecls,
|
|
/// whose initializers we want to walk into.
|
|
return Action::VisitNodeIf(isa<PatternBindingDecl>(D));
|
|
}
|
|
|
|
private:
|
|
DeclContext *currentClosureDC() const {
|
|
return ClosureDCs.empty() ? nullptr : ClosureDCs.back();
|
|
}
|
|
|
|
void inferVariables(Type type) {
|
|
type = type->getWithoutSpecifierType();
|
|
// Record the type variable itself because it has to
|
|
// be in scope even when already bound.
|
|
if (auto *typeVar = type->getAs<TypeVariableType>()) {
|
|
ReferencedVars.insert(typeVar);
|
|
|
|
// It is possible that contextual type of a parameter/result
|
|
// has been assigned to e.g. an anonymous or named argument
|
|
// early, to facilitate closure type checking. Such a
|
|
// type can have type variables inside e.g.
|
|
//
|
|
// func test<T>(_: (UnsafePointer<T>) -> Void) {}
|
|
//
|
|
// test { ptr in
|
|
// ...
|
|
// }
|
|
//
|
|
// Type variable representing `ptr` in the body of
|
|
// this closure would be bound to `UnsafePointer<$T>`
|
|
// in this case, where `$T` is a type variable for a
|
|
// generic parameter `T`.
|
|
type = CS.getFixedTypeRecursive(typeVar, /*wantRValue=*/false);
|
|
|
|
if (type->isEqual(typeVar))
|
|
return;
|
|
}
|
|
|
|
// Desugar type before collecting type variables, otherwise
|
|
// we can bring in scope unrelated type variables passed
|
|
// into the closure (via parameter/result) from contextual type.
|
|
// For example `Typealias<$T, $U>.Context` which desugars into
|
|
// `_Context<$U>` would bring in `$T` that could be inferrable
|
|
// only after the body of the closure is solved.
|
|
type = type->getCanonicalType();
|
|
|
|
// Don't walk into the opaque archetypes because they are not
|
|
// transparent in this context - `some P` could reference a
|
|
// type variables as substitutions which are visible only to
|
|
// the outer context.
|
|
if (type->is<OpaqueTypeArchetypeType>())
|
|
return;
|
|
|
|
if (type->hasTypeVariable()) {
|
|
SmallPtrSet<TypeVariableType *, 4> typeVars;
|
|
type->getTypeVariables(typeVars);
|
|
|
|
// Some of the type variables could be non-representative, so
|
|
// we need to recurse into `inferTypeVariables` to property
|
|
// handle them.
|
|
for (auto *typeVar : typeVars)
|
|
inferVariables(typeVar);
|
|
}
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
void ConjunctionElement::findReferencedVariables(
|
|
ConstraintSystem &cs, SmallPtrSetImpl<TypeVariableType *> &typeVars) const {
|
|
auto referencedVars = Element->getTypeVariables();
|
|
typeVars.insert(referencedVars.begin(), referencedVars.end());
|
|
|
|
if (Element->getKind() != ConstraintKind::SyntacticElement)
|
|
return;
|
|
|
|
ASTNode element = Element->getSyntacticElement();
|
|
auto *locator = Element->getLocator();
|
|
|
|
ASTNode parent = locator->getAnchor();
|
|
if (auto *SVE = getAsExpr<SingleValueStmtExpr>(parent)) {
|
|
// Use a parent closure if we have one. This is needed to correctly handle
|
|
// return statements that refer to an outer closure.
|
|
if (auto *CE = dyn_cast<ClosureExpr>(SVE->getDeclContext()))
|
|
parent = CE;
|
|
}
|
|
|
|
TypeVariableRefFinder refFinder(cs, parent, Element->getElementContext(),
|
|
typeVars);
|
|
|
|
// If this is a pattern of `for-in` statement, let's walk into `for-in`
|
|
// sequence expression because both elements are type-checked together.
|
|
//
|
|
// Correct expressions wouldn't have any type variables in sequence but
|
|
// they could appear due to circular references or other incorrect syntax.
|
|
if (isa<Pattern *>(element)) {
|
|
if (auto parent =
|
|
locator->getLastElementAs<LocatorPathElt::SyntacticElement>()) {
|
|
if (auto *forEach = getAsStmt<ForEachStmt>(parent->getElement())) {
|
|
if (auto *sequence = forEach->getSequence())
|
|
sequence->walk(refFinder);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto *patternBinding =
|
|
dyn_cast_or_null<PatternBindingDecl>(element.dyn_cast<Decl *>())) {
|
|
// Let's not walk into placeholder variable initializers, since they
|
|
// are type-checked separately right now.
|
|
if (isPlaceholderVar(patternBinding))
|
|
return;
|
|
|
|
if (auto patternBindingElt =
|
|
locator
|
|
->getLastElementAs<LocatorPathElt::PatternBindingElement>()) {
|
|
if (auto *init = patternBinding->getInit(patternBindingElt->getIndex()))
|
|
init->walk(refFinder);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (isa<Decl *>(element) || isa<StmtConditionElement *>(element) ||
|
|
isa<Expr *>(element) || element.isPattern(PatternKind::Expr) ||
|
|
element.isStmt(StmtKind::Return)) {
|
|
element.walk(refFinder);
|
|
}
|
|
} |