mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
5529 lines
190 KiB
C++
5529 lines
190 KiB
C++
//===--- ConstraintSystem.cpp - Constraint-based Type Checking ------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 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 the constraint-based type checker, anchored by the
|
|
// \c ConstraintSystem class, which provides type checking and type
|
|
// inference for expressions.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "swift/Sema/ConstraintSystem.h"
|
|
#include "CSDiagnostics.h"
|
|
#include "OpenedExistentials.h"
|
|
#include "TypeCheckAvailability.h"
|
|
#include "TypeCheckConcurrency.h"
|
|
#include "TypeCheckEmbedded.h"
|
|
#include "TypeCheckMacros.h"
|
|
#include "TypeCheckType.h"
|
|
#include "TypeChecker.h"
|
|
#include "swift/AST/ConformanceLookup.h"
|
|
#include "swift/AST/ExistentialLayout.h"
|
|
#include "swift/AST/GenericEnvironment.h"
|
|
#include "swift/AST/Initializer.h"
|
|
#include "swift/AST/MacroDefinition.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/ProtocolConformance.h"
|
|
#include "swift/AST/TypeCheckRequests.h"
|
|
#include "swift/AST/TypeTransform.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/Basic/Defer.h"
|
|
#include "swift/Basic/Statistic.h"
|
|
#include "swift/Sema/CSFix.h"
|
|
#include "swift/Sema/ConstraintGraph.h"
|
|
#include "swift/Sema/IDETypeChecking.h"
|
|
#include "swift/Sema/PreparedOverload.h"
|
|
#include "swift/Sema/SolutionResult.h"
|
|
#include "llvm/ADT/SetVector.h"
|
|
#include "llvm/ADT/SmallSet.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/Format.h"
|
|
#include <cmath>
|
|
|
|
using namespace swift;
|
|
using namespace constraints;
|
|
using namespace inference;
|
|
|
|
#define DEBUG_TYPE "ConstraintSystem"
|
|
|
|
ExpressionTimer::ExpressionTimer(AnchorType Anchor, ConstraintSystem &CS,
|
|
unsigned thresholdInSecs)
|
|
: Anchor(Anchor), Context(CS.getASTContext()),
|
|
StartTime(llvm::TimeRecord::getCurrentTime()),
|
|
ThresholdInSecs(thresholdInSecs),
|
|
PrintDebugTiming(CS.getASTContext().TypeCheckerOpts.DebugTimeExpressions),
|
|
PrintWarning(true) {}
|
|
|
|
SourceRange ExpressionTimer::getAffectedRange() const {
|
|
ASTNode anchor;
|
|
|
|
if (auto *locator = Anchor.dyn_cast<ConstraintLocator *>()) {
|
|
anchor = simplifyLocatorToAnchor(locator);
|
|
// If locator couldn't be simplified down to a single AST
|
|
// element, let's use its root.
|
|
if (!anchor)
|
|
anchor = locator->getAnchor();
|
|
} else {
|
|
anchor = cast<Expr *>(Anchor);
|
|
}
|
|
|
|
return anchor.getSourceRange();
|
|
}
|
|
|
|
ExpressionTimer::~ExpressionTimer() {
|
|
auto elapsed = getElapsedProcessTimeInFractionalSeconds();
|
|
unsigned elapsedMS = static_cast<unsigned>(elapsed * 1000);
|
|
|
|
if (PrintDebugTiming) {
|
|
// Round up to the nearest 100th of a millisecond.
|
|
llvm::errs() << llvm::format("%0.2f", std::ceil(elapsed * 100000) / 100)
|
|
<< "ms\t";
|
|
if (auto *E = Anchor.dyn_cast<Expr *>()) {
|
|
E->getLoc().print(llvm::errs(), Context.SourceMgr);
|
|
} else {
|
|
auto *locator = cast<ConstraintLocator *>(Anchor);
|
|
locator->dump(&Context.SourceMgr, llvm::errs());
|
|
}
|
|
llvm::errs() << "\n";
|
|
}
|
|
|
|
if (!PrintWarning)
|
|
return;
|
|
|
|
const auto WarnLimit = getWarnLimit();
|
|
|
|
if (WarnLimit == 0 || elapsedMS < WarnLimit)
|
|
return;
|
|
|
|
auto sourceRange = getAffectedRange();
|
|
|
|
if (sourceRange.Start.isValid()) {
|
|
Context.Diags
|
|
.diagnose(sourceRange.Start, diag::debug_long_expression, elapsedMS,
|
|
WarnLimit)
|
|
.highlight(sourceRange);
|
|
}
|
|
}
|
|
|
|
ConstraintSystem::ConstraintSystem(DeclContext *dc,
|
|
ConstraintSystemOptions options,
|
|
DiagnosticTransaction *diagnosticTransaction)
|
|
: Context(dc->getASTContext()), DC(dc), Options(options),
|
|
diagnosticTransaction(diagnosticTransaction),
|
|
Arena(dc->getASTContext(), Allocator),
|
|
CG(*this)
|
|
{
|
|
assert(DC && "context required");
|
|
// Respect the global debugging flag, but turn off debugging while
|
|
// parsing and loading other modules.
|
|
if (Context.TypeCheckerOpts.DebugConstraintSolver &&
|
|
DC->getParentModule()->isMainModule()) {
|
|
Options |= ConstraintSystemFlags::DebugConstraints;
|
|
}
|
|
if (Context.LangOpts.UseClangFunctionTypes)
|
|
Options |= ConstraintSystemFlags::UseClangFunctionTypes;
|
|
}
|
|
|
|
ConstraintSystem::~ConstraintSystem() {
|
|
for (unsigned i = 0, n = TypeVariables.size(); i != n; ++i) {
|
|
auto &impl = TypeVariables[i]->getImpl();
|
|
delete impl.getGraphNode();
|
|
impl.setGraphNode(nullptr);
|
|
}
|
|
}
|
|
|
|
void ConstraintSystem::startExpressionTimer(ExpressionTimer::AnchorType anchor) {
|
|
ASSERT(!Timer);
|
|
|
|
const auto &opts = getASTContext().TypeCheckerOpts;
|
|
unsigned timeout = opts.ExpressionTimeoutThreshold;
|
|
|
|
// If either the timeout is set, or we're asked to emit warnings,
|
|
// start the timer. Otherwise, don't start the timer, it's needless
|
|
// overhead.
|
|
if (timeout == 0) {
|
|
if (opts.WarnLongExpressionTypeChecking == 0)
|
|
return;
|
|
|
|
timeout = ExpressionTimer::NoLimit;
|
|
}
|
|
|
|
Timer.emplace(anchor, *this, timeout);
|
|
}
|
|
|
|
void ConstraintSystem::incrementScopeCounter() {
|
|
++NumSolverScopes;
|
|
if (auto *Stats = getASTContext().Stats)
|
|
++Stats->getFrontendCounters().NumConstraintScopes;
|
|
}
|
|
|
|
void ConstraintSystem::incrementLeafScopes() {
|
|
if (auto *Stats = getASTContext().Stats)
|
|
++Stats->getFrontendCounters().NumLeafScopes;
|
|
}
|
|
|
|
bool ConstraintSystem::hasFreeTypeVariables() {
|
|
// Look for any free type variables.
|
|
return llvm::any_of(TypeVariables, [](const TypeVariableType *typeVar) {
|
|
return !typeVar->getImpl().hasRepresentativeOrFixed();
|
|
});
|
|
}
|
|
|
|
void ConstraintSystem::addTypeVariable(TypeVariableType *typeVar) {
|
|
ASSERT(!PreparingOverload);
|
|
|
|
TypeVariables.insert(typeVar);
|
|
|
|
// Notify the constraint graph.
|
|
CG.addTypeVariable(typeVar);
|
|
}
|
|
|
|
void ConstraintSystem::mergeEquivalenceClasses(TypeVariableType *typeVar1,
|
|
TypeVariableType *typeVar2,
|
|
bool updateWorkList) {
|
|
assert(typeVar1 == getRepresentative(typeVar1) &&
|
|
"typeVar1 is not the representative");
|
|
assert(typeVar2 == getRepresentative(typeVar2) &&
|
|
"typeVar2 is not the representative");
|
|
assert(typeVar1 != typeVar2 && "cannot merge type with itself");
|
|
|
|
// Always merge 'up' the constraint stack, because it is simpler.
|
|
if (typeVar1->getImpl().getID() > typeVar2->getImpl().getID())
|
|
std::swap(typeVar1, typeVar2);
|
|
|
|
CG.mergeNodesPre(typeVar2);
|
|
typeVar1->getImpl().mergeEquivalenceClasses(typeVar2, getTrail());
|
|
CG.mergeNodes(typeVar1, typeVar2);
|
|
|
|
if (updateWorkList) {
|
|
addTypeVariableConstraintsToWorkList(typeVar1);
|
|
}
|
|
}
|
|
|
|
/// Determine whether the given type variables occurs in the given type.
|
|
bool ConstraintSystem::typeVarOccursInType(TypeVariableType *typeVar,
|
|
Type type,
|
|
bool *involvesOtherTypeVariables) {
|
|
SmallPtrSet<TypeVariableType *, 4> typeVars;
|
|
type->getTypeVariables(typeVars);
|
|
|
|
bool occurs = typeVars.count(typeVar);
|
|
if (involvesOtherTypeVariables) {
|
|
*involvesOtherTypeVariables =
|
|
occurs ? typeVars.size() > 1 : !typeVars.empty();
|
|
}
|
|
|
|
return occurs;
|
|
}
|
|
|
|
void ConstraintSystem::assignFixedType(TypeVariableType *typeVar, Type type,
|
|
bool updateState,
|
|
bool notifyBindingInference) {
|
|
assert(!type->hasError() &&
|
|
"Should not be assigning a type involving ErrorType!");
|
|
|
|
CG.retractFromInference(typeVar);
|
|
typeVar->getImpl().assignFixedType(type, getTrail());
|
|
|
|
if (!updateState)
|
|
return;
|
|
|
|
if (!type->isTypeVariableOrMember()) {
|
|
// If this type variable represents a literal, check whether we picked the
|
|
// default literal type. First, find the corresponding protocol.
|
|
//
|
|
// If we have the constraint graph, we can check all type variables in
|
|
// the equivalence class. This is the More Correct path.
|
|
// FIXME: Eliminate the less-correct path.
|
|
auto typeVarRep = getRepresentative(typeVar);
|
|
for (auto *tv : CG[typeVarRep].getEquivalenceClass()) {
|
|
auto locator = tv->getImpl().getLocator();
|
|
if (!(locator && (locator->directlyAt<CollectionExpr>() ||
|
|
locator->directlyAt<LiteralExpr>())))
|
|
continue;
|
|
|
|
auto *literalProtocol = TypeChecker::getLiteralProtocol(
|
|
getASTContext(), castToExpr(locator->getAnchor()));
|
|
if (!literalProtocol)
|
|
continue;
|
|
|
|
// If the protocol has a default type, check it.
|
|
if (auto defaultType = TypeChecker::getDefaultType(literalProtocol, DC)) {
|
|
auto isDefaultType = [&literalProtocol, &defaultType](Type type) {
|
|
// Treat `std.string` as a default type just like we do
|
|
// Swift standard library `String`. This helps to disambiguate
|
|
// operator overloads that use `std.string` vs. a custom C++
|
|
// type that conforms to `ExpressibleByStringLiteral` as well.
|
|
//
|
|
// This doesn't clash with String because inference won't attempt
|
|
// C++ types unless we discover them from a constraint and the
|
|
// optimizer and old hacks always prefer the actual default type.
|
|
if (literalProtocol->getKnownProtocolKind() ==
|
|
KnownProtocolKind::ExpressibleByStringLiteral &&
|
|
type->isCxxString()) {
|
|
return true;
|
|
}
|
|
|
|
// Check whether the nominal types match. This makes sure that we
|
|
// properly handle Array vs. Array<T>.
|
|
return defaultType->getAnyNominal() == type->getAnyNominal();
|
|
};
|
|
|
|
if (!isDefaultType(type))
|
|
increaseScore(SK_NonDefaultLiteral, locator);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Notify the constraint graph.
|
|
CG.bindTypeVariable(typeVar, type);
|
|
|
|
addTypeVariableConstraintsToWorkList(typeVar);
|
|
|
|
if (notifyBindingInference)
|
|
CG.introduceToInference(typeVar, type);
|
|
}
|
|
|
|
void ConstraintSystem::addTypeVariableConstraintsToWorkList(
|
|
TypeVariableType *typeVar) {
|
|
// Activate the constraints affected by a change to this type variable.
|
|
for (auto *constraint : CG.gatherAllConstraints(typeVar))
|
|
if (!constraint->isActive())
|
|
activateConstraint(constraint);
|
|
}
|
|
|
|
void ConstraintSystem::addConversionRestriction(
|
|
Type srcType, Type dstType,
|
|
ConversionRestrictionKind restriction) {
|
|
auto key = std::make_pair(srcType.getPointer(), dstType.getPointer());
|
|
bool inserted = ConstraintRestrictions.insert(
|
|
std::make_pair(key, restriction)).second;
|
|
if (!inserted)
|
|
return;
|
|
|
|
if (solverState) {
|
|
recordChange(SolverTrail::Change::AddedConversionRestriction(
|
|
srcType, dstType));
|
|
}
|
|
}
|
|
|
|
void ConstraintSystem::removeConversionRestriction(
|
|
Type srcType, Type dstType) {
|
|
auto key = std::make_pair(srcType.getPointer(), dstType.getPointer());
|
|
bool erased = ConstraintRestrictions.erase(key);
|
|
ASSERT(erased);
|
|
}
|
|
|
|
void ConstraintSystem::addFix(ConstraintFix *fix) {
|
|
ASSERT(!PreparingOverload);
|
|
|
|
bool inserted = Fixes.insert(fix);
|
|
ASSERT(inserted);
|
|
|
|
if (solverState)
|
|
recordChange(SolverTrail::Change::AddedFix(fix));
|
|
}
|
|
|
|
void ConstraintSystem::removeFix(ConstraintFix *fix) {
|
|
ASSERT(Fixes.back() == fix);
|
|
Fixes.pop_back();
|
|
}
|
|
|
|
void ConstraintSystem::recordDisjunctionChoice(
|
|
ConstraintLocator *locator, unsigned index) {
|
|
bool inserted = DisjunctionChoices.insert({locator, index}).second;
|
|
ASSERT(inserted);
|
|
|
|
if (solverState)
|
|
recordChange(SolverTrail::Change::RecordedDisjunctionChoice(locator));
|
|
}
|
|
|
|
void ConstraintSystem::recordAppliedDisjunction(
|
|
ConstraintLocator *locator, FunctionType *fnType) {
|
|
// We shouldn't ever register disjunction choices multiple times.
|
|
bool inserted = AppliedDisjunctions.insert(
|
|
std::make_pair(locator, fnType)).second;
|
|
ASSERT(inserted);
|
|
|
|
if (solverState)
|
|
recordChange(SolverTrail::Change::RecordedAppliedDisjunction(locator));
|
|
}
|
|
|
|
/// Retrieve a dynamic result signature for the given declaration.
|
|
static std::tuple<char, ObjCSelector, CanType>
|
|
getDynamicResultSignature(ValueDecl *decl) {
|
|
// Handle functions.
|
|
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
|
|
// Strip `@Sendable` and `any Sendable` because it should be
|
|
// possible to add them without affecting lookup results and
|
|
// shadowing. All of the declarations that are processed here
|
|
// are `@objc` and hence `@preconcurrency`.
|
|
auto type = func->getMethodInterfaceType()->stripConcurrency(
|
|
/*recursive=*/true, /*dropGlobalActor=*/false);
|
|
return std::make_tuple(func->isStatic(), func->getObjCSelector(),
|
|
type->getCanonicalType());
|
|
}
|
|
|
|
if (auto asd = dyn_cast<AbstractStorageDecl>(decl)) {
|
|
auto ty = asd->getInterfaceType();
|
|
|
|
// Strip off a generic signature if we have one. This matches the logic
|
|
// for methods, and ensures that we don't take a protocol's generic
|
|
// signature into account for a subscript requirement.
|
|
if (auto *genericFn = ty->getAs<GenericFunctionType>()) {
|
|
ty = FunctionType::get(genericFn->getParams(), genericFn->getResult(),
|
|
genericFn->getExtInfo());
|
|
}
|
|
|
|
// Handle properties and subscripts, anchored by the getter's selector.
|
|
return std::make_tuple(asd->isStatic(), asd->getObjCGetterSelector(),
|
|
ty->getCanonicalType());
|
|
}
|
|
|
|
llvm_unreachable("Not a valid @objc member");
|
|
}
|
|
|
|
LookupResult &ConstraintSystem::lookupMember(Type base, DeclNameRef name,
|
|
SourceLoc loc) {
|
|
// Check whether we've already performed this lookup.
|
|
auto &result = MemberLookups[{base, name}];
|
|
if (result) return *result;
|
|
|
|
// Lookup the member.
|
|
result = TypeChecker::lookupMember(
|
|
DC, base, name, loc, defaultConstraintSolverMemberLookupOptions);
|
|
|
|
// If we are in an @_unsafeInheritExecutor context, swap out
|
|
// declarations for their _unsafeInheritExecutor_ counterparts if they
|
|
// exist.
|
|
if (enclosingUnsafeInheritsExecutor(DC)) {
|
|
introduceUnsafeInheritExecutorReplacements(DC, base, loc, *result);
|
|
}
|
|
|
|
// If we aren't performing dynamic lookup, we're done.
|
|
if (!*result || !base->isAnyObject())
|
|
return *result;
|
|
|
|
// We are performing dynamic lookup. Filter out redundant results early.
|
|
llvm::DenseMap<std::tuple<char, ObjCSelector, CanType>, ValueDecl *> known;
|
|
bool anyRemovals = false;
|
|
for (const auto &entry : *result) {
|
|
auto *decl = entry.getValueDecl();
|
|
|
|
// Remove invalid declarations so the constraint solver doesn't need to
|
|
// cope with them.
|
|
if (decl->isInvalid()) {
|
|
anyRemovals = true;
|
|
continue;
|
|
}
|
|
|
|
// If this is the first entry with the signature, record it.
|
|
auto &uniqueEntry = known[getDynamicResultSignature(decl)];
|
|
if (!uniqueEntry) {
|
|
uniqueEntry = decl;
|
|
continue;
|
|
}
|
|
|
|
// We have duplication; note that we'll need to remove something,
|
|
anyRemovals = true;
|
|
|
|
// If the entry we recorded was unavailable but this new entry is not,
|
|
// replace the recorded entry with this one.
|
|
if (isDeclUnavailable(uniqueEntry) && !isDeclUnavailable(decl)) {
|
|
uniqueEntry = decl;
|
|
}
|
|
}
|
|
|
|
// If there's anything to remove, filter it out now.
|
|
if (anyRemovals) {
|
|
result->filter([&](LookupResultEntry entry, bool isOuter) -> bool {
|
|
auto *decl = entry.getValueDecl();
|
|
|
|
// Remove invalid declarations so the constraint solver doesn't need to
|
|
// cope with them.
|
|
if (decl->isInvalid())
|
|
return false;
|
|
|
|
return known[getDynamicResultSignature(decl)] == decl;
|
|
});
|
|
}
|
|
|
|
return *result;
|
|
}
|
|
|
|
ArrayRef<Type>
|
|
ConstraintSystem::getAlternativeLiteralTypes(KnownProtocolKind kind,
|
|
SmallVectorImpl<Type> &scratch) {
|
|
assert(scratch.empty());
|
|
if (kind == KnownProtocolKind::ExpressibleByIntegerLiteral) {
|
|
// Integer literals can be treated as floating point literals.
|
|
if (auto floatProto = getASTContext().getProtocol(
|
|
KnownProtocolKind::ExpressibleByFloatLiteral)) {
|
|
if (auto defaultType = TypeChecker::getDefaultType(floatProto, DC))
|
|
scratch.push_back(defaultType);
|
|
}
|
|
}
|
|
return scratch;
|
|
}
|
|
|
|
bool ConstraintSystem::containsIDEInspectionTarget(ASTNode node) const {
|
|
return swift::containsIDEInspectionTarget(node.getSourceRange(),
|
|
Context.SourceMgr);
|
|
}
|
|
|
|
bool ConstraintSystem::containsIDEInspectionTarget(
|
|
const ArgumentList *args) const {
|
|
return swift::containsIDEInspectionTarget(args->getSourceRange(),
|
|
Context.SourceMgr);
|
|
}
|
|
|
|
void ConstraintSystem::recordPotentialThrowSite(
|
|
CatchNode catchNode, PotentialThrowSite site) {
|
|
potentialThrowSites.push_back({catchNode, site});
|
|
if (solverState)
|
|
recordChange(SolverTrail::Change::RecordedPotentialThrowSite(catchNode));
|
|
}
|
|
|
|
void ConstraintSystem::removePotentialThrowSite(CatchNode catchNode) {
|
|
ASSERT(potentialThrowSites.back().first == catchNode);
|
|
potentialThrowSites.pop_back();
|
|
}
|
|
|
|
void ConstraintSystem::recordPotentialThrowSite(
|
|
PotentialThrowSite::Kind kind, Type type,
|
|
ConstraintLocatorBuilder locator) {
|
|
ASTContext &ctx = getASTContext();
|
|
|
|
// Only record potential throw sites when typed throws is enabled.
|
|
if (!ctx.LangOpts.hasFeature(Feature::FullTypedThrows))
|
|
return;
|
|
|
|
// Catch node location is determined by the source location.
|
|
auto sourceLoc = locator.getAnchor().getStartLoc();
|
|
if (!sourceLoc)
|
|
return;
|
|
|
|
auto catchNode = ASTScope::lookupCatchNode(DC->getParentModule(), sourceLoc);
|
|
if (!catchNode)
|
|
return;
|
|
|
|
// If there is an explicit caught type for this node, we don't need to
|
|
// record a potential throw site.
|
|
if (Type explicitCaughtType = catchNode.getExplicitCaughtType(ctx))
|
|
return;
|
|
|
|
// do..catch statements without an explicit `throws` clause do infer
|
|
// thrown types.
|
|
if (isa<DoCatchStmt *>(catchNode)) {
|
|
PotentialThrowSite site{kind, type, getConstraintLocator(locator)};
|
|
recordPotentialThrowSite(catchNode, site);
|
|
return;
|
|
}
|
|
|
|
// Closures without an explicit `throws` clause, and which syntactically
|
|
// appear that they can throw, do infer thrown types.
|
|
auto closure = cast<ClosureExpr *>(catchNode);
|
|
|
|
// Check whether the closure syntactically throws. If not, there is no
|
|
// need to record a throw site.
|
|
if (!closureEffects(closure).isThrowing())
|
|
return;
|
|
|
|
PotentialThrowSite site{kind, type, getConstraintLocator(locator)};
|
|
recordPotentialThrowSite(catchNode, site);
|
|
}
|
|
|
|
Type ConstraintSystem::getExplicitCaughtErrorType(CatchNode catchNode) {
|
|
ASTContext &ctx = getASTContext();
|
|
|
|
// If there is an explicit caught type for this node, use it.
|
|
if (Type explicitCaughtType = catchNode.getExplicitCaughtType(ctx)) {
|
|
if (explicitCaughtType->hasTypeParameter())
|
|
explicitCaughtType = DC->mapTypeIntoContext(explicitCaughtType);
|
|
|
|
return explicitCaughtType;
|
|
}
|
|
|
|
return Type();
|
|
}
|
|
|
|
Type ConstraintSystem::getCaughtErrorType(CatchNode catchNode) {
|
|
ASTContext &ctx = getASTContext();
|
|
|
|
// If we have an explicit caught error type for this node, use it.
|
|
if (auto explicitCaughtType = getExplicitCaughtErrorType(catchNode))
|
|
return explicitCaughtType;
|
|
|
|
// Retrieve the thrown error type of a closure.
|
|
// FIXME: This will need to change when we do inference of thrown error
|
|
// types in closures.
|
|
if (auto closure = catchNode.dyn_cast<ClosureExpr *>()) {
|
|
return getClosureType(closure)->getEffectiveThrownErrorTypeOrNever();
|
|
}
|
|
|
|
if (!ctx.LangOpts.hasFeature(Feature::FullTypedThrows))
|
|
return ctx.getErrorExistentialType();
|
|
|
|
// Handle inference of caught error types.
|
|
|
|
// Collect all of the potential throw sites for this catch node.
|
|
SmallVector<PotentialThrowSite, 2> throwSites;
|
|
for (const auto &potentialThrowSite : potentialThrowSites) {
|
|
if (potentialThrowSite.first == catchNode) {
|
|
throwSites.push_back(potentialThrowSite.second);
|
|
}
|
|
}
|
|
|
|
Type caughtErrorType = ctx.getNeverType();
|
|
for (const auto &throwSite : throwSites) {
|
|
Type type = simplifyType(throwSite.type);
|
|
|
|
Type thrownErrorType;
|
|
switch (throwSite.kind) {
|
|
case PotentialThrowSite::Application: {
|
|
auto fnType = type->castTo<AnyFunctionType>();
|
|
thrownErrorType = fnType->getEffectiveThrownErrorTypeOrNever();
|
|
break;
|
|
}
|
|
|
|
case PotentialThrowSite::ExplicitThrow:
|
|
case PotentialThrowSite::NonExhaustiveDoCatch:
|
|
case PotentialThrowSite::PropertyAccess:
|
|
thrownErrorType = type;
|
|
break;
|
|
}
|
|
|
|
// Perform the errorUnion() of the caught error type so far with the
|
|
// thrown error type of this potential throw site.
|
|
caughtErrorType = TypeChecker::errorUnion(
|
|
caughtErrorType, thrownErrorType,
|
|
[&](Type type) {
|
|
return simplifyType(type);
|
|
});
|
|
|
|
// If we ended up at 'any Error', we're done.
|
|
if (caughtErrorType->isErrorExistentialType())
|
|
break;
|
|
}
|
|
|
|
return caughtErrorType;
|
|
}
|
|
|
|
ConstraintLocator *ConstraintSystem::getConstraintLocator(
|
|
ASTNode anchor, ArrayRef<ConstraintLocator::PathElement> path) {
|
|
auto summaryFlags = ConstraintLocator::getSummaryFlagsForPath(path);
|
|
return getConstraintLocator(anchor, path, summaryFlags);
|
|
}
|
|
|
|
ConstraintLocator *ConstraintSystem::getConstraintLocator(
|
|
ASTNode anchor, ArrayRef<ConstraintLocator::PathElement> path,
|
|
unsigned summaryFlags) {
|
|
assert(summaryFlags == ConstraintLocator::getSummaryFlagsForPath(path));
|
|
|
|
// Check whether a locator with this anchor + path already exists.
|
|
llvm::FoldingSetNodeID id;
|
|
ConstraintLocator::Profile(id, anchor, path);
|
|
void *insertPos = nullptr;
|
|
auto locator = ConstraintLocators.FindNodeOrInsertPos(id, insertPos);
|
|
if (locator)
|
|
return locator;
|
|
|
|
// Allocate a new locator and add it to the set.
|
|
locator = ConstraintLocator::create(getAllocator(), anchor, path,
|
|
summaryFlags);
|
|
ConstraintLocators.InsertNode(locator, insertPos);
|
|
return locator;
|
|
}
|
|
|
|
ConstraintLocator *ConstraintSystem::getConstraintLocator(
|
|
const ConstraintLocatorBuilder &builder) {
|
|
// If the builder has an empty path, just extract its base locator.
|
|
if (builder.hasEmptyPath()) {
|
|
return builder.getBaseLocator();
|
|
}
|
|
|
|
// We have to build a new locator. Extract the paths from the builder.
|
|
SmallVector<LocatorPathElt, 4> path;
|
|
auto anchor = builder.getLocatorParts(path);
|
|
return getConstraintLocator(anchor, path, builder.getSummaryFlags());
|
|
}
|
|
|
|
ConstraintLocator *ConstraintSystem::getConstraintLocator(
|
|
ConstraintLocator *locator,
|
|
ArrayRef<ConstraintLocator::PathElement> newElts) {
|
|
auto oldPath = locator->getPath();
|
|
SmallVector<ConstraintLocator::PathElement, 4> newPath;
|
|
newPath.append(oldPath.begin(), oldPath.end());
|
|
newPath.append(newElts.begin(), newElts.end());
|
|
return getConstraintLocator(locator->getAnchor(), newPath);
|
|
}
|
|
|
|
ConstraintLocator *ConstraintSystem::getConstraintLocator(
|
|
const ConstraintLocatorBuilder &builder,
|
|
ArrayRef<ConstraintLocator::PathElement> newElts) {
|
|
SmallVector<ConstraintLocator::PathElement, 4> newPath;
|
|
auto anchor = builder.getLocatorParts(newPath);
|
|
newPath.append(newElts.begin(), newElts.end());
|
|
return getConstraintLocator(anchor, newPath);
|
|
}
|
|
|
|
ConstraintLocator *ConstraintSystem::getImplicitValueConversionLocator(
|
|
ConstraintLocatorBuilder root, ConversionRestrictionKind restriction) {
|
|
SmallVector<LocatorPathElt, 4> path;
|
|
auto anchor = root.getLocatorParts(path);
|
|
{
|
|
if (isExpr<DictionaryExpr>(anchor) && path.size() > 1) {
|
|
// Drop everything except for first `tuple element #`.
|
|
path.pop_back_n(path.size() - 1);
|
|
}
|
|
|
|
// Drop any value-to-optional conversions that were applied along the
|
|
// way to reach this one.
|
|
while (!path.empty()) {
|
|
if (path.back().is<LocatorPathElt::OptionalInjection>()) {
|
|
path.pop_back();
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// If conversion is for a tuple element, let's drop `TupleType`
|
|
// components from the path since they carry information for
|
|
// diagnostics that `ExprRewriter` won't be able to re-construct
|
|
// during solution application.
|
|
if (!path.empty() && path.back().is<LocatorPathElt::TupleElement>()) {
|
|
path.erase(llvm::remove_if(path,
|
|
[](const LocatorPathElt &elt) {
|
|
return elt.is<LocatorPathElt::TupleType>();
|
|
}),
|
|
path.end());
|
|
}
|
|
}
|
|
|
|
return getConstraintLocator(/*base=*/getConstraintLocator(anchor, path),
|
|
LocatorPathElt::ImplicitConversion(restriction));
|
|
}
|
|
|
|
ConstraintLocator *ConstraintSystem::getCalleeLocator(
|
|
ConstraintLocator *locator, bool lookThroughApply,
|
|
llvm::function_ref<Type(Expr *)> getType,
|
|
llvm::function_ref<Type(Type)> simplifyType,
|
|
llvm::function_ref<std::optional<SelectedOverload>(ConstraintLocator *)>
|
|
getOverloadFor) {
|
|
if (locator->findLast<LocatorPathElt::ImplicitConversion>())
|
|
return locator;
|
|
|
|
auto anchor = locator->getAnchor();
|
|
auto path = locator->getPath();
|
|
{
|
|
// If we have an implicit x[dynamicMember:] subscript call, the callee
|
|
// is given by the original member locator it is based on, which we can get
|
|
// by stripping away the implicit member element and everything after it.
|
|
auto iter = path.rbegin();
|
|
using ImplicitSubscriptElt = LocatorPathElt::ImplicitDynamicMemberSubscript;
|
|
if (locator->findLast<ImplicitSubscriptElt>(iter)) {
|
|
auto newPath = path.drop_back(iter - path.rbegin() + 1);
|
|
return getConstraintLocator(anchor, newPath);
|
|
}
|
|
}
|
|
|
|
{
|
|
// If we have a locator for a member found through key path dynamic member
|
|
// lookup, then we need to chop off the elements after the
|
|
// KeyPathDynamicMember element to get the callee locator.
|
|
auto iter = path.rbegin();
|
|
if (locator->findLast<LocatorPathElt::KeyPathDynamicMember>(iter)) {
|
|
auto newPath = path.drop_back(iter - path.rbegin());
|
|
return getConstraintLocator(anchor, newPath);
|
|
}
|
|
}
|
|
|
|
{
|
|
// Pattern match is always a callee regardless of what comes after it.
|
|
auto iter = path.rbegin();
|
|
if (locator->findLast<LocatorPathElt::PatternMatch>(iter)) {
|
|
auto newPath = path.drop_back(iter - path.rbegin());
|
|
return getConstraintLocator(anchor, newPath);
|
|
}
|
|
}
|
|
|
|
if (locator->findLast<LocatorPathElt::DynamicCallable>()) {
|
|
return getConstraintLocator(anchor, LocatorPathElt::ApplyFunction());
|
|
}
|
|
|
|
if (locator->isLastElement<LocatorPathElt::ArgumentAttribute>()) {
|
|
auto argLoc = getConstraintLocator(anchor, path.drop_back());
|
|
return getCalleeLocator(argLoc, lookThroughApply, getType, simplifyType,
|
|
getOverloadFor);
|
|
}
|
|
|
|
// If we have a locator that starts with a key path component element, we
|
|
// may have a callee given by a property, subscript, initializer, or method
|
|
// component.
|
|
if (auto componentElt =
|
|
locator->getFirstElementAs<LocatorPathElt::KeyPathComponent>()) {
|
|
auto *kpExpr = castToExpr<KeyPathExpr>(anchor);
|
|
auto component = kpExpr->getComponents()[componentElt->getIndex()];
|
|
|
|
using ComponentKind = KeyPathExpr::Component::Kind;
|
|
switch (component.getKind()) {
|
|
case ComponentKind::UnresolvedSubscript:
|
|
case ComponentKind::Subscript:
|
|
// For a subscript the callee is given by 'component -> subscript member'.
|
|
return getConstraintLocator(
|
|
anchor, {*componentElt, ConstraintLocator::SubscriptMember});
|
|
case ComponentKind::UnresolvedMember:
|
|
case ComponentKind::Member:
|
|
// For a property, the choice is just given by the component.
|
|
return getConstraintLocator(anchor, *componentElt);
|
|
case ComponentKind::TupleElement:
|
|
llvm_unreachable("Not implemented by CSGen");
|
|
break;
|
|
case ComponentKind::UnresolvedApply:
|
|
case ComponentKind::Apply:
|
|
return getConstraintLocator(anchor, *componentElt);
|
|
case ComponentKind::Invalid:
|
|
case ComponentKind::OptionalForce:
|
|
case ComponentKind::OptionalChain:
|
|
case ComponentKind::OptionalWrap:
|
|
case ComponentKind::Identity:
|
|
case ComponentKind::DictionaryKey:
|
|
case ComponentKind::CodeCompletion:
|
|
// These components don't have any callee associated, so just continue.
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Make sure we handle subscripts before looking at apply exprs. We don't
|
|
// want to return a subscript member locator for an expression such as x[](y),
|
|
// as its callee is not the subscript, but rather the function it returns.
|
|
if (isExpr<SubscriptExpr>(anchor))
|
|
return getConstraintLocator(anchor, ConstraintLocator::SubscriptMember);
|
|
|
|
auto getSpecialFnCalleeLoc = [&](Type fnTy) -> ConstraintLocator * {
|
|
fnTy = simplifyType(fnTy);
|
|
// It's okay for function type to contain type variable(s) e.g.
|
|
// opened generic function types, but not to be one.
|
|
assert(!fnTy->is<TypeVariableType>());
|
|
|
|
// For an apply of a metatype, we have a short-form constructor. Unlike
|
|
// other locators to callees, these are anchored on the apply expression
|
|
// rather than the function expr.
|
|
if (fnTy->is<AnyMetatypeType>()) {
|
|
return getConstraintLocator(anchor,
|
|
{LocatorPathElt::ApplyFunction(),
|
|
LocatorPathElt::ConstructorMember()});
|
|
}
|
|
|
|
// Handle an apply of a nominal type which supports callAsFunction.
|
|
if (fnTy->isCallAsFunctionType(DC)) {
|
|
return getConstraintLocator(anchor,
|
|
{LocatorPathElt::ApplyFunction(),
|
|
LocatorPathElt::ImplicitCallAsFunction()});
|
|
}
|
|
|
|
// Handling an apply for a nominal type that supports @dynamicCallable.
|
|
if (fnTy->hasDynamicCallableAttribute()) {
|
|
return getConstraintLocator(anchor, LocatorPathElt::ApplyFunction());
|
|
}
|
|
|
|
return nullptr;
|
|
};
|
|
|
|
if (lookThroughApply) {
|
|
if (auto *applyExpr = getAsExpr<ApplyExpr>(anchor)) {
|
|
auto *fnExpr = applyExpr->getFn();
|
|
|
|
// Handle special cases for applies of non-function types.
|
|
if (auto *loc = getSpecialFnCalleeLoc(getType(fnExpr)))
|
|
return loc;
|
|
|
|
// Otherwise fall through and look for locators anchored on the function
|
|
// expr. For CallExprs, this can look through things like parens and
|
|
// optional chaining.
|
|
if (auto *callExpr = getAsExpr<CallExpr>(anchor)) {
|
|
anchor = callExpr->getDirectCallee();
|
|
} else {
|
|
anchor = fnExpr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto *UDE = getAsExpr<UnresolvedDotExpr>(anchor)) {
|
|
if (UDE->isImplicit() &&
|
|
UDE->getName().getBaseName() == Context.Id_callAsFunction) {
|
|
return getConstraintLocator(anchor,
|
|
{LocatorPathElt::ApplyFunction(),
|
|
LocatorPathElt::ImplicitCallAsFunction()});
|
|
}
|
|
|
|
return getConstraintLocator(
|
|
anchor, TypeChecker::getSelfForInitDelegationInConstructor(DC, UDE)
|
|
? ConstraintLocator::ConstructorMember
|
|
: ConstraintLocator::Member);
|
|
}
|
|
|
|
if (auto *UME = getAsExpr<UnresolvedMemberExpr>(anchor)) {
|
|
return getConstraintLocator(UME, ConstraintLocator::UnresolvedMember);
|
|
}
|
|
|
|
if (isExpr<MemberRefExpr>(anchor))
|
|
return getConstraintLocator(anchor, ConstraintLocator::Member);
|
|
|
|
if (isExpr<ObjectLiteralExpr>(anchor))
|
|
return getConstraintLocator(anchor, ConstraintLocator::ConstructorMember);
|
|
|
|
if (locator->isFirstElement<LocatorPathElt::CoercionOperand>()) {
|
|
auto *CE = castToExpr<CoerceExpr>(anchor);
|
|
locator = getConstraintLocator(CE->getSubExpr()->getValueProvidingExpr(),
|
|
path.drop_front());
|
|
return getCalleeLocator(locator, lookThroughApply, getType, simplifyType,
|
|
getOverloadFor);
|
|
}
|
|
|
|
if (auto FVE = getAsExpr<ForceValueExpr>(anchor))
|
|
return getConstraintLocator(FVE->getSubExpr(), ConstraintLocator::Member);
|
|
|
|
return getConstraintLocator(anchor);
|
|
}
|
|
|
|
ConstraintLocator *ConstraintSystem::getOpenOpaqueLocator(
|
|
ConstraintLocatorBuilder locator, OpaqueTypeDecl *opaqueDecl) {
|
|
// Use only the opaque type declaration.
|
|
return getConstraintLocator(
|
|
ASTNode(opaqueDecl),
|
|
{ LocatorPathElt::OpenedOpaqueArchetype(opaqueDecl) }, 0);
|
|
}
|
|
|
|
std::pair<Type, ExistentialArchetypeType *>
|
|
ConstraintSystem::openAnyExistentialType(Type type,
|
|
ConstraintLocator *locator) {
|
|
Type result = ExistentialArchetypeType::getAny(type);
|
|
Type t = result;
|
|
while (t->is<MetatypeType>())
|
|
t = t->getMetatypeInstanceType();
|
|
auto *opened = t->castTo<ExistentialArchetypeType>();
|
|
|
|
recordOpenedExistentialType(locator, opened);
|
|
|
|
return {result, opened};
|
|
}
|
|
|
|
void ConstraintSystem::recordOpenedExistentialType(
|
|
ConstraintLocator *locator,
|
|
ExistentialArchetypeType *opened,
|
|
PreparedOverloadBuilder *preparedOverload) {
|
|
if (preparedOverload) {
|
|
preparedOverload->openedExistentialType(opened);
|
|
return;
|
|
}
|
|
|
|
bool inserted = OpenedExistentialTypes.insert({locator, opened}).second;
|
|
ASSERT(inserted);
|
|
|
|
if (solverState)
|
|
recordChange(SolverTrail::Change::RecordedOpenedExistentialType(locator));
|
|
}
|
|
|
|
GenericEnvironment *
|
|
ConstraintSystem::getPackExpansionEnvironment(PackExpansionExpr *expr) const {
|
|
auto result = PackExpansionEnvironments.find(expr);
|
|
if (result == PackExpansionEnvironments.end())
|
|
return nullptr;
|
|
|
|
return result->second;
|
|
}
|
|
|
|
GenericEnvironment *ConstraintSystem::createPackExpansionEnvironment(
|
|
PackExpansionExpr *expr, CanGenericTypeParamType shapeParam) {
|
|
auto *contextEnv = DC->getGenericEnvironmentOfContext();
|
|
auto elementSig = getASTContext().getOpenedElementSignature(
|
|
contextEnv->getGenericSignature().getCanonicalSignature(), shapeParam);
|
|
auto contextSubs = contextEnv->getForwardingSubstitutionMap();
|
|
auto *env = GenericEnvironment::forOpenedElement(elementSig, UUID::fromTime(),
|
|
shapeParam, contextSubs);
|
|
recordPackExpansionEnvironment(expr, env);
|
|
return env;
|
|
}
|
|
|
|
void ConstraintSystem::recordPackExpansionEnvironment(PackExpansionExpr *expr,
|
|
GenericEnvironment *env) {
|
|
bool inserted = PackExpansionEnvironments.insert({expr, env}).second;
|
|
ASSERT(inserted);
|
|
|
|
if (solverState)
|
|
recordChange(SolverTrail::Change::RecordedPackExpansionEnvironment(expr));
|
|
}
|
|
|
|
PackExpansionExpr *
|
|
ConstraintSystem::getPackElementExpansion(PackElementExpr *packElement) const {
|
|
const auto match = PackElementExpansions.find(packElement);
|
|
return (match == PackElementExpansions.end()) ? nullptr : match->second;
|
|
}
|
|
|
|
void ConstraintSystem::recordPackElementExpansion(
|
|
PackElementExpr *packElement, PackExpansionExpr *packExpansion) {
|
|
bool inserted =
|
|
PackElementExpansions.insert({packElement, packExpansion}).second;
|
|
ASSERT(inserted);
|
|
|
|
if (solverState) {
|
|
recordChange(
|
|
SolverTrail::Change::RecordedPackElementExpansion(packElement));
|
|
}
|
|
}
|
|
|
|
/// Extend the given depth map by adding depths for all of the subexpressions
|
|
/// of the given expression.
|
|
static void extendDepthMap(
|
|
Expr *expr,
|
|
llvm::DenseMap<Expr *, std::pair<unsigned, Expr *>> &depthMap) {
|
|
// If we already have an entry in the map, we don't need to update it. This
|
|
// avoids invalidating previous entries when solving a smaller component of a
|
|
// larger AST node, e.g during conjunction solving.
|
|
if (depthMap.contains(expr))
|
|
return;
|
|
|
|
class RecordingTraversal : public ASTWalker {
|
|
SmallVector<ClosureExpr *, 4> Closures;
|
|
|
|
public:
|
|
llvm::DenseMap<Expr *, std::pair<unsigned, Expr *>> &DepthMap;
|
|
unsigned Depth = 0;
|
|
|
|
explicit RecordingTraversal(
|
|
llvm::DenseMap<Expr *, std::pair<unsigned, Expr *>> &depthMap)
|
|
: DepthMap(depthMap) {}
|
|
|
|
MacroWalking getMacroWalkingBehavior() const override {
|
|
return MacroWalking::ArgumentsAndExpansion;
|
|
}
|
|
|
|
// For argument lists, bump the depth of the arguments, as they are
|
|
// effectively nested within the argument list. It's debatable whether we
|
|
// should actually do this, as it doesn't reflect the true expression depth,
|
|
// but it's needed to preserve compatibility with the behavior from when
|
|
// TupleExpr and ParenExpr were used to represent argument lists.
|
|
PreWalkResult<ArgumentList *>
|
|
walkToArgumentListPre(ArgumentList *ArgList) override {
|
|
++Depth;
|
|
return Action::Continue(ArgList);
|
|
}
|
|
PostWalkResult<ArgumentList *>
|
|
walkToArgumentListPost(ArgumentList *ArgList) override {
|
|
--Depth;
|
|
return Action::Continue(ArgList);
|
|
}
|
|
|
|
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
|
|
DepthMap[E] = {Depth, Parent.getAsExpr()};
|
|
++Depth;
|
|
|
|
if (auto CE = dyn_cast<ClosureExpr>(E))
|
|
Closures.push_back(CE);
|
|
|
|
return Action::Continue(E);
|
|
}
|
|
|
|
PostWalkResult<Expr *> walkToExprPost(Expr *E) override {
|
|
if (auto CE = dyn_cast<ClosureExpr>(E)) {
|
|
assert(Closures.back() == CE);
|
|
Closures.pop_back();
|
|
}
|
|
|
|
--Depth;
|
|
return Action::Continue(E);
|
|
}
|
|
|
|
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
|
|
if (auto RS = dyn_cast<ReturnStmt>(S)) {
|
|
// For return statements, treat the parent of the return expression
|
|
// as the closure itself.
|
|
if (RS->hasResult() && !Closures.empty()) {
|
|
llvm::SaveAndRestore<ParentTy> SavedParent(Parent, Closures.back());
|
|
auto E = RS->getResult();
|
|
E->walk(*this);
|
|
return Action::SkipNode(S);
|
|
}
|
|
}
|
|
|
|
return Action::Continue(S);
|
|
}
|
|
};
|
|
|
|
RecordingTraversal traversal(depthMap);
|
|
expr->walk(traversal);
|
|
}
|
|
|
|
std::optional<std::pair<unsigned, Expr *>>
|
|
ConstraintSystem::getExprDepthAndParent(Expr *expr) {
|
|
// Bring the set of expression weights up to date.
|
|
while (NumInputExprsInWeights < InputExprs.size()) {
|
|
extendDepthMap(InputExprs[NumInputExprsInWeights], ExprWeights);
|
|
++NumInputExprsInWeights;
|
|
}
|
|
|
|
auto e = ExprWeights.find(expr);
|
|
if (e != ExprWeights.end())
|
|
return e->second;
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<std::pair<Type, Type>>
|
|
ConstraintSystem::isDictionaryType(Type type) {
|
|
if (auto boundStruct = type->getAs<BoundGenericStructType>()) {
|
|
if (boundStruct->getDecl() == type->getASTContext().getDictionaryDecl()) {
|
|
auto genericArgs = boundStruct->getGenericArgs();
|
|
return std::make_pair(genericArgs[0], genericArgs[1]);
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Type> ConstraintSystem::isSetType(Type type) {
|
|
if (auto boundStruct = type->getAs<BoundGenericStructType>()) {
|
|
if (boundStruct->getDecl() == type->getASTContext().getSetDecl())
|
|
return boundStruct->getGenericArgs()[0];
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
Type ConstraintSystem::getFixedTypeRecursive(Type type, TypeMatchOptions &flags,
|
|
bool wantRValue) {
|
|
ASSERT(!PreparingOverload);
|
|
|
|
if (wantRValue)
|
|
type = type->getRValueType();
|
|
|
|
if (auto depMemType = type->getAs<DependentMemberType>()) {
|
|
auto baseTy = depMemType->getBase();
|
|
if (!baseTy->hasTypeVariable() && !baseTy->hasDependentMember())
|
|
return type;
|
|
|
|
// FIXME: Perform a more limited simplification?
|
|
Type newType = simplifyType(type);
|
|
if (newType.getPointer() == type.getPointer()) return type;
|
|
|
|
// Once we've simplified a dependent member type, we need to generate a
|
|
// new constraint.
|
|
flags |= TMF_GenerateConstraints;
|
|
|
|
return getFixedTypeRecursive(newType, flags, wantRValue);
|
|
}
|
|
|
|
// Tuple types can lose their tuple structure under substitution
|
|
// when a parameter pack is substituted with one element.
|
|
if (type->is<TupleType>()) {
|
|
auto simplified = simplifyType(type);
|
|
if (simplified.getPointer() == type.getPointer())
|
|
return type;
|
|
|
|
return getFixedTypeRecursive(simplified, flags, wantRValue);
|
|
}
|
|
|
|
if (type->is<AnyMetatypeType>()) {
|
|
auto simplified = simplifyType(type);
|
|
if (simplified.getPointer() == type.getPointer())
|
|
return type;
|
|
|
|
return getFixedTypeRecursive(simplified, flags, wantRValue);
|
|
}
|
|
|
|
if (auto typeVar = type->getAs<TypeVariableType>()) {
|
|
if (auto fixed = getFixedType(typeVar))
|
|
return getFixedTypeRecursive(fixed, flags, wantRValue);
|
|
|
|
return getRepresentative(typeVar);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
TypeVariableType *ConstraintSystem::isRepresentativeFor(
|
|
TypeVariableType *typeVar, ConstraintLocator::PathElementKind kind) const {
|
|
// We only attempt to look for this if type variable is
|
|
// a representative.
|
|
if (getRepresentative(typeVar) != typeVar)
|
|
return nullptr;
|
|
|
|
auto &CG = const_cast<ConstraintSystem *>(this)->getConstraintGraph();
|
|
auto &result = CG[typeVar];
|
|
auto equivalence = result.getEquivalenceClass();
|
|
auto member = llvm::find_if(equivalence, [=](TypeVariableType *eq) {
|
|
auto *loc = eq->getImpl().getLocator();
|
|
if (!loc)
|
|
return false;
|
|
|
|
auto path = loc->getPath();
|
|
return !path.empty() && path.back().getKind() == kind;
|
|
});
|
|
|
|
if (member == equivalence.end())
|
|
return nullptr;
|
|
|
|
return *member;
|
|
}
|
|
|
|
static std::optional<std::pair<VarDecl *, Type>>
|
|
getPropertyWrapperInformationFromOverload(
|
|
SelectedOverload resolvedOverload, DeclContext *DC,
|
|
llvm::function_ref<std::optional<std::pair<VarDecl *, Type>>(VarDecl *)>
|
|
getInformation) {
|
|
if (auto *decl =
|
|
dyn_cast_or_null<VarDecl>(resolvedOverload.choice.getDeclOrNull())) {
|
|
if (auto declInformation = getInformation(decl)) {
|
|
Type type;
|
|
VarDecl *memberDecl;
|
|
std::tie(memberDecl, type) = *declInformation;
|
|
if (Type baseType = resolvedOverload.choice.getBaseType()) {
|
|
type = baseType->getRValueType()->getTypeOfMember(memberDecl, type);
|
|
}
|
|
return std::make_pair(decl, type);
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<std::pair<VarDecl *, Type>>
|
|
ConstraintSystem::getPropertyWrapperProjectionInfo(
|
|
SelectedOverload resolvedOverload) {
|
|
return getPropertyWrapperInformationFromOverload(
|
|
resolvedOverload, DC,
|
|
[](VarDecl *decl) -> std::optional<std::pair<VarDecl *, Type>> {
|
|
if (!decl->hasAttachedPropertyWrapper())
|
|
return std::nullopt;
|
|
|
|
auto projectionVar = decl->getPropertyWrapperProjectionVar();
|
|
if (!projectionVar)
|
|
return std::nullopt;
|
|
|
|
return std::make_pair(projectionVar,
|
|
projectionVar->getInterfaceType());
|
|
});
|
|
}
|
|
|
|
std::optional<std::pair<VarDecl *, Type>>
|
|
ConstraintSystem::getPropertyWrapperInformation(
|
|
SelectedOverload resolvedOverload) {
|
|
return getPropertyWrapperInformationFromOverload(
|
|
resolvedOverload, DC,
|
|
[](VarDecl *decl) -> std::optional<std::pair<VarDecl *, Type>> {
|
|
if (!decl->hasAttachedPropertyWrapper())
|
|
return std::nullopt;
|
|
|
|
auto backingTy = decl->getPropertyWrapperBackingPropertyType();
|
|
if (!backingTy)
|
|
return std::nullopt;
|
|
|
|
return std::make_pair(decl, backingTy);
|
|
});
|
|
}
|
|
|
|
std::optional<std::pair<VarDecl *, Type>>
|
|
ConstraintSystem::getWrappedPropertyInformation(
|
|
SelectedOverload resolvedOverload) {
|
|
return getPropertyWrapperInformationFromOverload(
|
|
resolvedOverload, DC,
|
|
[](VarDecl *decl) -> std::optional<std::pair<VarDecl *, Type>> {
|
|
if (auto wrapped = decl->getOriginalWrappedProperty())
|
|
return std::make_pair(decl, wrapped->getInterfaceType());
|
|
|
|
return std::nullopt;
|
|
});
|
|
}
|
|
|
|
void ConstraintSystem::addOverloadSet(Type boundType,
|
|
ArrayRef<OverloadChoice> choices,
|
|
DeclContext *useDC,
|
|
ConstraintLocator *locator,
|
|
std::optional<unsigned> favoredIndex) {
|
|
// If there is a single choice, add the bind overload directly.
|
|
if (choices.size() == 1) {
|
|
addBindOverloadConstraint(boundType, choices.front(), locator, useDC);
|
|
return;
|
|
}
|
|
|
|
SmallVector<Constraint *, 4> candidates;
|
|
generateOverloadConstraints(candidates, boundType, choices, useDC, locator,
|
|
favoredIndex);
|
|
// For an overload set (disjunction) from newly generated candidates.
|
|
addOverloadSet(candidates, locator);
|
|
}
|
|
|
|
void ConstraintSystem::addOverloadSet(ArrayRef<Constraint *> choices,
|
|
ConstraintLocator *locator) {
|
|
assert(!choices.empty() && "Empty overload set");
|
|
|
|
// If there is a single choice, attempt it right away.
|
|
if (choices.size() == 1) {
|
|
simplifyConstraint(*choices.front());
|
|
return;
|
|
}
|
|
|
|
auto *disjunction =
|
|
Constraint::createDisjunction(*this, choices, locator, ForgetChoice);
|
|
addUnsolvedConstraint(disjunction);
|
|
if (simplifyAppliedOverloads(disjunction, locator))
|
|
retireFailedConstraint(disjunction);
|
|
}
|
|
|
|
FunctionType::ExtInfo ConstraintSystem::closureEffects(ClosureExpr *expr) {
|
|
return evaluateOrDefault(
|
|
getASTContext().evaluator, ClosureEffectsRequest{expr},
|
|
FunctionType::ExtInfo());
|
|
}
|
|
|
|
FunctionType::ExtInfo ClosureEffectsRequest::evaluate(
|
|
Evaluator &evaluator, ClosureExpr *expr) const {
|
|
// A walker that looks for 'try' and 'throw' expressions
|
|
// that aren't nested within closures, nested declarations,
|
|
// or exhaustive catches.
|
|
class FindInnerThrows : public ASTWalker {
|
|
DeclContext *DC;
|
|
bool FoundThrow = false;
|
|
|
|
MacroWalking getMacroWalkingBehavior() const override {
|
|
return MacroWalking::Expansion;
|
|
}
|
|
|
|
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
|
|
// If we've found a 'try', record it and terminate the traversal.
|
|
if (isa<TryExpr>(expr)) {
|
|
FoundThrow = true;
|
|
return Action::Stop();
|
|
}
|
|
|
|
// Don't walk into a 'try!' or 'try?'.
|
|
if (isa<ForceTryExpr>(expr) || isa<OptionalTryExpr>(expr)) {
|
|
return Action::SkipNode(expr);
|
|
}
|
|
|
|
// Do not recurse into other closures.
|
|
if (isa<ClosureExpr>(expr))
|
|
return Action::SkipNode(expr);
|
|
|
|
return Action::Continue(expr);
|
|
}
|
|
|
|
PreWalkAction walkToDeclPre(Decl *decl) override {
|
|
// Do not walk into function or type declarations.
|
|
return Action::VisitNodeIf(isa<PatternBindingDecl>(decl));
|
|
}
|
|
|
|
bool isSyntacticallyExhaustive(DoCatchStmt *stmt) {
|
|
for (auto catchClause : stmt->getCatches()) {
|
|
for (auto &LabelItem : catchClause->getMutableCaseLabelItems()) {
|
|
if (isSyntacticallyExhaustive(catchClause->getStartLoc(),
|
|
LabelItem))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isSyntacticallyExhaustive(SourceLoc CatchLoc,
|
|
CaseLabelItem &LabelItem) {
|
|
// If it's obviously non-exhaustive, great.
|
|
if (LabelItem.getGuardExpr())
|
|
return false;
|
|
|
|
// If we can show that it's exhaustive without full
|
|
// type-checking, great.
|
|
if (LabelItem.isSyntacticallyExhaustive())
|
|
return true;
|
|
|
|
// Okay, resolve the pattern.
|
|
Pattern *pattern = LabelItem.getPattern();
|
|
if (!LabelItem.isPatternResolved()) {
|
|
pattern = TypeChecker::resolvePattern(pattern, DC,
|
|
/*isStmtCondition*/false);
|
|
if (!pattern) return false;
|
|
|
|
// Save that aside while we explore the type.
|
|
LabelItem.setPattern(pattern, /*resolved=*/true);
|
|
}
|
|
|
|
// Require the pattern to have a particular shape: a number
|
|
// of is-patterns applied to an irrefutable pattern.
|
|
pattern = pattern->getSemanticsProvidingPattern();
|
|
while (auto isp = dyn_cast<IsPattern>(pattern)) {
|
|
Type castType;
|
|
if (auto castTypeRepr = isp->getCastTypeRepr()) {
|
|
castType = TypeResolution::resolveContextualType(
|
|
castTypeRepr, DC, TypeResolverContext::InExpression,
|
|
/*unboundTyOpener*/ nullptr,
|
|
/*placeholderHandler*/ nullptr,
|
|
/*packElementOpener*/ nullptr);
|
|
} else {
|
|
castType = isp->getCastType();
|
|
}
|
|
|
|
if (castType->hasError()) {
|
|
return false;
|
|
}
|
|
|
|
if (!isp->hasSubPattern()) {
|
|
pattern = nullptr;
|
|
break;
|
|
} else {
|
|
pattern = isp->getSubPattern()->getSemanticsProvidingPattern();
|
|
}
|
|
}
|
|
if (pattern && pattern->isRefutablePattern()) {
|
|
return false;
|
|
}
|
|
|
|
// Okay, now it should be safe to coerce the pattern.
|
|
// Pull the top-level pattern back out.
|
|
pattern = LabelItem.getPattern();
|
|
|
|
auto &ctx = DC->getASTContext();
|
|
if (!ctx.getErrorDecl())
|
|
return false;
|
|
|
|
auto contextualPattern =
|
|
ContextualPattern::forRawPattern(pattern, DC);
|
|
pattern = TypeChecker::coercePatternToType(
|
|
contextualPattern, ctx.getErrorExistentialType(),
|
|
TypeResolverContext::InExpression);
|
|
if (!pattern)
|
|
return false;
|
|
|
|
LabelItem.setPattern(pattern, /*resolved=*/true);
|
|
return LabelItem.isSyntacticallyExhaustive();
|
|
}
|
|
|
|
PreWalkResult<Stmt *> walkToStmtPre(Stmt *stmt) override {
|
|
// If we've found a 'throw', record it and terminate the traversal.
|
|
if (isa<ThrowStmt>(stmt)) {
|
|
FoundThrow = true;
|
|
return Action::Stop();
|
|
}
|
|
|
|
// Handle do/catch differently.
|
|
if (auto doCatch = dyn_cast<DoCatchStmt>(stmt)) {
|
|
// Only walk into the 'do' clause of a do/catch statement
|
|
// if the catch isn't syntactically exhaustive.
|
|
if (!isSyntacticallyExhaustive(doCatch)) {
|
|
if (!doCatch->getBody()->walk(*this))
|
|
return Action::Stop();
|
|
}
|
|
|
|
// Walk into all the catch clauses.
|
|
for (auto catchClause : doCatch->getCatches()) {
|
|
if (!catchClause->walk(*this))
|
|
return Action::Stop();
|
|
}
|
|
|
|
// We've already walked all the children we care about.
|
|
return Action::SkipNode(stmt);
|
|
}
|
|
|
|
if (auto forEach = dyn_cast<ForEachStmt>(stmt)) {
|
|
if (forEach->getTryLoc().isValid()) {
|
|
FoundThrow = true;
|
|
return Action::Stop();
|
|
}
|
|
}
|
|
|
|
return Action::Continue(stmt);
|
|
}
|
|
|
|
public:
|
|
FindInnerThrows(DeclContext *dc)
|
|
: DC(dc) {}
|
|
|
|
bool foundThrow() { return FoundThrow; }
|
|
};
|
|
|
|
// If either 'throws' or 'async' was explicitly specified, use that
|
|
// set of effects.
|
|
bool throws = expr->getThrowsLoc().isValid();
|
|
bool async = expr->getAsyncLoc().isValid();
|
|
bool sendable = expr->getAttrs().hasAttribute<SendableAttr>();
|
|
|
|
if (throws || async) {
|
|
if (expr->getThrowsLoc().isValid() && !expr->getExplicitThrownTypeRepr())
|
|
diagnoseUntypedThrowsInEmbedded(expr, expr->getThrowsLoc());
|
|
|
|
return ASTExtInfoBuilder()
|
|
.withThrows(throws, /*FIXME:*/Type())
|
|
.withAsync(async)
|
|
.withSendable(sendable)
|
|
.build();
|
|
}
|
|
|
|
// Scan the body to determine the effects.
|
|
auto body = expr->getBody();
|
|
if (!body)
|
|
return ASTExtInfoBuilder().withSendable(sendable).build();
|
|
|
|
auto throwFinder = FindInnerThrows(expr);
|
|
body->walk(throwFinder);
|
|
return ASTExtInfoBuilder()
|
|
.withThrows(throwFinder.foundThrow(), /*FIXME:*/Type())
|
|
.withAsync(bool(findAsyncNode(expr)))
|
|
.withSendable(sendable)
|
|
.build();
|
|
}
|
|
|
|
bool ConstraintSystem::isAsynchronousContext(DeclContext *dc) {
|
|
if (auto func = dyn_cast<AbstractFunctionDecl>(dc))
|
|
return func->isAsyncContext();
|
|
|
|
if (auto closure = dyn_cast<ClosureExpr>(dc)) {
|
|
return evaluateOrDefault(
|
|
getASTContext().evaluator,
|
|
ClosureEffectsRequest{const_cast<ClosureExpr *>(closure)},
|
|
FunctionType::ExtInfo()).isAsync();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ConstraintSystem::buildDisjunctionForOptionalVsUnderlying(
|
|
Type boundTy, Type ty, ConstraintLocator *locator) {
|
|
// NOTE: If we use other locator kinds for these disjunctions, we
|
|
// need to account for it in solution scores for forced-unwraps.
|
|
assert(locator->getPath().back().getKind() ==
|
|
ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice ||
|
|
locator->getPath().back().getKind() ==
|
|
ConstraintLocator::DynamicLookupResult);
|
|
assert(!ty->is<InOutType>());
|
|
auto rvalueTy = ty->getWithoutSpecifierType();
|
|
|
|
// If the type to bind is a placeholder, we can propagate it, as we don't know
|
|
// whether it can be optional or non-optional, and we would have already
|
|
// recorded a fix for it.
|
|
if (rvalueTy->isPlaceholder()) {
|
|
addConstraint(ConstraintKind::Bind, boundTy, ty, locator);
|
|
return;
|
|
}
|
|
|
|
// Create the constraint to bind to the optional type and make it the favored
|
|
// choice.
|
|
auto *bindToOptional =
|
|
Constraint::create(*this, ConstraintKind::Bind, boundTy, ty, locator);
|
|
bindToOptional->setFavored();
|
|
|
|
auto underlyingType = rvalueTy->getOptionalObjectType();
|
|
if (!underlyingType) {
|
|
// If we don't have an optional, `ty` hasn't been resolved yet.
|
|
auto *typeVar = rvalueTy->castTo<TypeVariableType>();
|
|
auto *locator = typeVar->getImpl().getLocator();
|
|
|
|
// We need to allocate a type variable to represent an object type of a
|
|
// future optional, and add a constraint between `ty` and `underlyingType`
|
|
// to model it.
|
|
underlyingType = createTypeVariable(
|
|
getConstraintLocator(locator, LocatorPathElt::GenericArgument(0)),
|
|
TVO_PrefersSubtypeBinding | TVO_CanBindToLValue |
|
|
TVO_CanBindToNoEscape);
|
|
|
|
// Using a `typeVar` here because l-value is going to be applied
|
|
// to the underlying type below.
|
|
addConstraint(ConstraintKind::OptionalObject, typeVar, underlyingType,
|
|
locator);
|
|
}
|
|
if (ty->is<LValueType>())
|
|
underlyingType = LValueType::get(underlyingType);
|
|
|
|
auto *bindToUnderlying = Constraint::create(*this, ConstraintKind::Bind,
|
|
boundTy, underlyingType, locator);
|
|
|
|
llvm::SmallVector<Constraint *, 2> choices = {bindToOptional,
|
|
bindToUnderlying};
|
|
|
|
// Create the disjunction
|
|
addDisjunctionConstraint(choices, locator, RememberChoice);
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct TypeSimplifier {
|
|
ConstraintSystem &CS;
|
|
llvm::function_ref<Type(TypeVariableType *)> GetFixedTypeFn;
|
|
|
|
struct ActivePackExpansion {
|
|
bool isPackExpansion = false;
|
|
unsigned index = 0;
|
|
};
|
|
SmallVector<ActivePackExpansion, 4> ActivePackExpansions;
|
|
|
|
TypeSimplifier(ConstraintSystem &CS,
|
|
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn)
|
|
: CS(CS), GetFixedTypeFn(getFixedTypeFn) {}
|
|
|
|
std::optional<Type> operator()(TypeBase *type) {
|
|
if (auto tvt = dyn_cast<TypeVariableType>(type)) {
|
|
auto fixedTy = GetFixedTypeFn(tvt);
|
|
|
|
// TODO: the following logic should be applied when rewriting
|
|
// PackElementType.
|
|
if (ActivePackExpansions.empty()) {
|
|
return fixedTy;
|
|
}
|
|
|
|
if (auto fixedPack = fixedTy->getAs<PackType>()) {
|
|
auto &activeExpansion = ActivePackExpansions.back();
|
|
if (activeExpansion.index >= fixedPack->getNumElements()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto fixedElt = fixedPack->getElementType(activeExpansion.index);
|
|
auto fixedExpansion = fixedElt->getAs<PackExpansionType>();
|
|
if (activeExpansion.isPackExpansion && fixedExpansion) {
|
|
return fixedExpansion->getPatternType();
|
|
} else if (!activeExpansion.isPackExpansion && !fixedExpansion) {
|
|
return fixedElt;
|
|
} else {
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
return fixedTy;
|
|
}
|
|
|
|
if (auto tuple = dyn_cast<TupleType>(type)) {
|
|
if (tuple->getNumElements() == 1) {
|
|
auto element = tuple->getElement(0);
|
|
auto elementType = element.getType();
|
|
auto resolvedType = elementType.transformRec(*this);
|
|
|
|
// If this is a single-element tuple with pack expansion
|
|
// variable inside, let's unwrap it if pack is flattened.
|
|
if (!element.hasName()) {
|
|
if (auto *typeVar = elementType->getAs<TypeVariableType>()) {
|
|
if (typeVar->getImpl().isPackExpansion() &&
|
|
!resolvedType->isEqual(typeVar) &&
|
|
!resolvedType->is<PackExpansionType>() &&
|
|
!resolvedType->is<PackType>() &&
|
|
!resolvedType->is<PackArchetypeType>()) {
|
|
return resolvedType;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Flatten single-element tuples containing type variables that cannot
|
|
// bind to packs.
|
|
auto typeVar = resolvedType->getAs<TypeVariableType>();
|
|
if (!element.hasName() && typeVar &&
|
|
!typeVar->getImpl().canBindToPack() &&
|
|
!typeVar->getImpl().isPackExpansion()) {
|
|
return typeVar;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto expansion = dyn_cast<PackExpansionType>(type)) {
|
|
auto patternType = expansion->getPatternType();
|
|
// First, let's check whether pattern type has all of the type variables
|
|
// that represent packs resolved, otherwise we don't have enough information
|
|
// to flatten this pack expansion type.
|
|
//
|
|
// Note that we don't actually need to do deep transformation here
|
|
// because pack variables can only appear in structural positions.
|
|
if (patternType.findIf([&](Type type) {
|
|
if (auto *typeVar = type->getAs<TypeVariableType>()) {
|
|
if (typeVar->getImpl().canBindToPack())
|
|
return GetFixedTypeFn(typeVar)->is<TypeVariableType>();
|
|
}
|
|
return false;
|
|
})) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Transform the count type, ignoring any active pack expansions.
|
|
auto countType = expansion->getCountType().transformRec(
|
|
TypeSimplifier(CS, GetFixedTypeFn));
|
|
|
|
if (!countType->is<PackType>() &&
|
|
!countType->is<PackArchetypeType>()) {
|
|
SmallVector<Type, 2> rootParameterPacks;
|
|
countType->getTypeParameterPacks(rootParameterPacks);
|
|
if (!rootParameterPacks.empty())
|
|
countType = rootParameterPacks[0];
|
|
}
|
|
|
|
// If both pattern and count are resolves, let's just return
|
|
// the pattern type for `transformWithPosition` to take care
|
|
// of the rest.
|
|
if (patternType->is<PackType>() && countType->is<PackType>())
|
|
return patternType;
|
|
|
|
if (auto countPack = countType->getAs<PackType>()) {
|
|
SmallVector<Type, 4> elts;
|
|
ActivePackExpansions.push_back({false, 0});
|
|
for (auto countElt : countPack->getElementTypes()) {
|
|
auto countExpansion = countElt->getAs<PackExpansionType>();
|
|
ActivePackExpansions.back().isPackExpansion =
|
|
(countExpansion != nullptr);
|
|
|
|
auto elt = expansion->getPatternType().transformRec(*this);
|
|
if (countExpansion)
|
|
elt = PackExpansionType::get(elt, countExpansion->getCountType());
|
|
elts.push_back(elt);
|
|
|
|
ActivePackExpansions.back().index++;
|
|
}
|
|
ActivePackExpansions.pop_back();
|
|
|
|
if (elts.size() == 1)
|
|
return elts[0];
|
|
return PackType::get(CS.getASTContext(), elts);
|
|
} else {
|
|
ActivePackExpansions.push_back({true, 0});
|
|
auto patternType = expansion->getPatternType().transformRec(*this);
|
|
ActivePackExpansions.pop_back();
|
|
return PackExpansionType::get(patternType, countType);
|
|
}
|
|
}
|
|
|
|
// If this is a dependent member type for which we end up simplifying
|
|
// the base to a non-type-variable, perform lookup.
|
|
if (auto depMemTy = dyn_cast<DependentMemberType>(type)) {
|
|
// Simplify the base.
|
|
Type newBase = depMemTy->getBase().transformRec(*this);
|
|
|
|
if (newBase->isPlaceholder()) {
|
|
return PlaceholderType::get(CS.getASTContext(), depMemTy);
|
|
}
|
|
|
|
// If nothing changed, we're done.
|
|
if (newBase.getPointer() == depMemTy->getBase().getPointer())
|
|
return std::nullopt;
|
|
|
|
// Dependent member types should only be created for associated types.
|
|
auto assocType = depMemTy->getAssocType();
|
|
assert(depMemTy->getAssocType() && "Expected associated type!");
|
|
|
|
// FIXME: It's kind of weird in general that we have to look
|
|
// through lvalue, inout and IUO types here
|
|
Type lookupBaseType = newBase->getWithoutSpecifierType();
|
|
if (auto selfType = lookupBaseType->getAs<DynamicSelfType>())
|
|
lookupBaseType = selfType->getSelfType();
|
|
|
|
if (lookupBaseType->mayHaveMembers() ||
|
|
lookupBaseType->is<PackType>()) {
|
|
auto *proto = assocType->getProtocol();
|
|
auto conformance = CS.lookupConformance(lookupBaseType, proto);
|
|
if (!conformance) {
|
|
// Special case: When building slab literals, we go through the same
|
|
// array literal machinery, so there will be a conversion constraint
|
|
// for the element to ExpressibleByArrayLiteral.ArrayLiteralType.
|
|
if (lookupBaseType->isInlineArray()) {
|
|
auto &ctx = CS.getASTContext();
|
|
auto arrayProto =
|
|
ctx.getProtocol(KnownProtocolKind::ExpressibleByArrayLiteral);
|
|
auto elementAssocTy = arrayProto->getAssociatedTypeMembers()[0];
|
|
|
|
if (proto == arrayProto && assocType == elementAssocTy) {
|
|
return lookupBaseType->getInlineArrayElementType();
|
|
}
|
|
}
|
|
|
|
// If the base type doesn't conform to the associatedtype's protocol,
|
|
// there will be a missing conformance fix applied in diagnostic mode,
|
|
// so the concrete dependent member type is considered a "hole" in
|
|
// order to continue solving.
|
|
auto memberTy = DependentMemberType::get(lookupBaseType, assocType);
|
|
if (CS.shouldAttemptFixes() &&
|
|
CS.getPhase() == ConstraintSystemPhase::Solving) {
|
|
return PlaceholderType::get(CS.getASTContext(), memberTy);
|
|
}
|
|
|
|
return memberTy;
|
|
}
|
|
|
|
auto result = conformance.getTypeWitness(assocType);
|
|
if (result && !result->hasError())
|
|
return result;
|
|
}
|
|
|
|
return DependentMemberType::get(lookupBaseType, assocType);
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
Type ConstraintSystem::simplifyTypeImpl(Type type,
|
|
llvm::function_ref<Type(TypeVariableType *)> getFixedTypeFn) {
|
|
return type.transformRec(TypeSimplifier(*this, getFixedTypeFn));
|
|
}
|
|
|
|
Type ConstraintSystem::simplifyType(Type type) {
|
|
ASSERT(!PreparingOverload);
|
|
|
|
if (!type->hasTypeVariable())
|
|
return type;
|
|
|
|
// Map type variables down to the fixed types of their representatives.
|
|
return simplifyTypeImpl(type,
|
|
[&](TypeVariableType *tvt) -> Type {
|
|
if (auto fixed = getFixedType(tvt))
|
|
return simplifyType(fixed);
|
|
|
|
return getRepresentative(tvt);
|
|
});
|
|
}
|
|
|
|
void Solution::recordSingleArgMatchingChoice(ConstraintLocator *locator) {
|
|
auto &cs = getConstraintSystem();
|
|
assert(argumentMatchingChoices.find(locator) ==
|
|
argumentMatchingChoices.end() &&
|
|
"recording multiple bindings for same locator");
|
|
argumentMatchingChoices.insert(
|
|
{cs.getConstraintLocator(locator, ConstraintLocator::ApplyArgument),
|
|
MatchCallArgumentResult::forArity(1)});
|
|
}
|
|
|
|
Type Solution::simplifyType(Type type, bool wantInterfaceType) const {
|
|
// If we've been asked for an interface type, start by mapping any archetypes
|
|
// out of context.
|
|
if (wantInterfaceType)
|
|
type = type->mapTypeOutOfContext();
|
|
|
|
if (!type->hasTypeVariableOrPlaceholder())
|
|
return type;
|
|
|
|
// Map type variables to fixed types from bindings.
|
|
auto &cs = getConstraintSystem();
|
|
auto resolvedType =
|
|
cs.simplifyTypeImpl(type, [&](TypeVariableType *tvt) -> Type {
|
|
// If we want the interface type, use the generic parameter if we
|
|
// have one, otherwise map the fixed type out of context.
|
|
if (wantInterfaceType) {
|
|
if (auto *gp = tvt->getImpl().getGenericParameter())
|
|
return gp;
|
|
return getFixedType(tvt)->mapTypeOutOfContext();
|
|
}
|
|
return getFixedType(tvt);
|
|
});
|
|
ASSERT(!(wantInterfaceType && resolvedType->hasPrimaryArchetype()));
|
|
|
|
// We may have type variables and placeholders left over. These are solver
|
|
// allocated so cannot escape this function. Turn them into UnresolvedType.
|
|
// - Type variables may still be present from unresolved pack expansions where
|
|
// e.g the count type is a hole, so the pattern may never become a
|
|
// concrete type.
|
|
// - Placeholders may be present for any holes.
|
|
if (resolvedType->hasTypeVariableOrPlaceholder()) {
|
|
auto &ctx = cs.getASTContext();
|
|
resolvedType =
|
|
resolvedType.transformRec([&](Type type) -> std::optional<Type> {
|
|
if (!type->hasTypeVariableOrPlaceholder())
|
|
return type;
|
|
|
|
auto *typePtr = type.getPointer();
|
|
if (isa<TypeVariableType>(typePtr) || isa<PlaceholderType>(typePtr))
|
|
return Type(ctx.TheUnresolvedType);
|
|
|
|
return std::nullopt;
|
|
});
|
|
}
|
|
|
|
ASSERT(!resolvedType->getRecursiveProperties().isSolverAllocated());
|
|
return resolvedType;
|
|
}
|
|
|
|
Type Solution::simplifyTypeForCodeCompletion(Type Ty) const {
|
|
auto &CS = getConstraintSystem();
|
|
|
|
// First, instantiate all type variables that we know, but don't replace
|
|
// placeholders by unresolved types.
|
|
Ty = CS.simplifyTypeImpl(Ty, [this](TypeVariableType *typeVar) -> Type {
|
|
return getFixedType(typeVar);
|
|
});
|
|
|
|
// Next, replace all placeholders by type variables. We know that all type
|
|
// variables now in the type originate from placeholders.
|
|
Ty = Ty.transformRec([](Type type) -> std::optional<Type> {
|
|
if (auto *placeholder = type->getAs<PlaceholderType>()) {
|
|
if (auto *typeVar =
|
|
placeholder->getOriginator().dyn_cast<TypeVariableType *>()) {
|
|
return Type(typeVar);
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
});
|
|
|
|
// Replace all type variables (which must come from placeholders) by their
|
|
// generic parameters. Because we call into simplifyTypeImpl
|
|
Ty = CS.simplifyTypeImpl(Ty, [&CS, this](TypeVariableType *typeVar) -> Type {
|
|
// Code completion depends on generic parameter type being represented in
|
|
// terms of `ArchetypeType` since it's easy to extract protocol requirements
|
|
// from it.
|
|
auto getTypeVarAsArchetype = [](TypeVariableType *typeVar) -> Type {
|
|
if (auto *GP = typeVar->getImpl().getGenericParameter()) {
|
|
if (auto *GPD = GP->getDecl()) {
|
|
return GPD->getInnermostDeclContext()->mapTypeIntoContext(GP);
|
|
}
|
|
}
|
|
return Type();
|
|
};
|
|
|
|
if (auto archetype = getTypeVarAsArchetype(typeVar)) {
|
|
return archetype;
|
|
}
|
|
|
|
// Sometimes the type variable itself doesn't have have an originator that
|
|
// can be replaced by an archetype but one of its equivalent type variable
|
|
// does.
|
|
// Search thorough all equivalent type variables, looking for one that can
|
|
// be replaced by a generic parameter.
|
|
std::vector<std::pair<TypeVariableType *, Type>> bindings(
|
|
typeBindings.begin(), typeBindings.end());
|
|
// Make sure we iterate the bindings in a deterministic order.
|
|
llvm::sort(bindings, [](const std::pair<TypeVariableType *, Type> &lhs,
|
|
const std::pair<TypeVariableType *, Type> &rhs) {
|
|
return lhs.first->getID() < rhs.first->getID();
|
|
});
|
|
for (auto binding : bindings) {
|
|
if (auto placeholder = binding.second->getAs<PlaceholderType>()) {
|
|
if (placeholder->getOriginator().dyn_cast<TypeVariableType *>() ==
|
|
typeVar) {
|
|
if (auto archetype = getTypeVarAsArchetype(binding.first)) {
|
|
return archetype;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// When applying the logic below to get contextual types inside result
|
|
// builders, the code completion type variable is connected by a one-way
|
|
// constraint to a type variable in the buildBlock call, but that is not the
|
|
// type variable that represents the argument type. We need to find the type
|
|
// variable representing the argument to retrieve protocol requirements from
|
|
// it. Look for a ArgumentConversion constraint that allows us to retrieve
|
|
// the argument type var.
|
|
auto &cg = CS.getConstraintGraph();
|
|
|
|
// FIXME: The type variable is not going to be part of the constraint graph
|
|
// at this point unless it was created at the outermost decision level;
|
|
// otherwise it has already been rolled back! Work around this by creating
|
|
// an empty node if one doesn't exist.
|
|
cg.addTypeVariable(typeVar);
|
|
|
|
for (auto argConstraint : cg[typeVar].getConstraints()) {
|
|
if (argConstraint->getKind() == ConstraintKind::ArgumentConversion &&
|
|
argConstraint->getFirstType()->getRValueType()->isEqual(typeVar)) {
|
|
if (auto argTV =
|
|
argConstraint->getSecondType()->getAs<TypeVariableType>()) {
|
|
if (auto archetype = getTypeVarAsArchetype(argTV)) {
|
|
return archetype;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return typeVar;
|
|
});
|
|
|
|
// Logic to determine the contextual type inside buildBlock result builders:
|
|
//
|
|
// When completing inside a result builder, the result builder
|
|
// @ViewBuilder var body: some View {
|
|
// Text("Foo")
|
|
// #^COMPLETE^#
|
|
// }
|
|
// gets rewritten to
|
|
// @ViewBuilder var body: some View {
|
|
// let $__builder2: Text
|
|
// let $__builder0 = Text("Foo")
|
|
// let $__builder1 = #^COMPLETE^#
|
|
// $__builder2 = ViewBuilder.buildBlock($__builder0, $__builder1)
|
|
// return $__builder2
|
|
// }
|
|
// Inside the constraint system
|
|
// let $__builder1 = #^COMPLETE^#
|
|
// gets type checked without context, so we can't know the contextual type for
|
|
// the code completion token. But we know that $__builder1 (and thus the type
|
|
// of #^COMPLETE^#) is used as the second argument to ViewBuilder.buildBlock,
|
|
// so we can extract the contextual type from that call. To do this, figure
|
|
// out the type variable that is used for $__builder1 in the buildBlock call.
|
|
// This type variable is connected to the type variable of $__builder1's
|
|
// definition by a one-way constraint.
|
|
if (auto TV = Ty->getAs<TypeVariableType>()) {
|
|
for (auto constraint : CS.getConstraintGraph()[TV].getConstraints()) {
|
|
if (constraint->getKind() == ConstraintKind::OneWayEqual &&
|
|
constraint->getSecondType()->isEqual(TV)) {
|
|
return simplifyTypeForCodeCompletion(constraint->getFirstType());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove any remaining type variables and placeholders
|
|
Ty = simplifyType(Ty);
|
|
|
|
return Ty->getRValueType();
|
|
}
|
|
|
|
template <typename T>
|
|
static inline size_t size_in_bytes(const T &x) {
|
|
return (x.size() * (sizeof(typename T::key_type) + sizeof(unsigned))) +
|
|
(x.size() * (sizeof(typename T::value_type)));
|
|
}
|
|
|
|
size_t Solution::getTotalMemory() const {
|
|
if (TotalMemory)
|
|
return *TotalMemory;
|
|
|
|
// clang-format off
|
|
const_cast<Solution *>(this)->TotalMemory
|
|
= sizeof(*this) + size_in_bytes(typeBindings) +
|
|
overloadChoices.getMemorySize() +
|
|
ConstraintRestrictions.getMemorySize() +
|
|
(Fixes.size() * sizeof(void *)) + DisjunctionChoices.getMemorySize() +
|
|
AppliedDisjunctions.getMemorySize() +
|
|
OpenedTypes.getMemorySize() + OpenedExistentialTypes.getMemorySize() +
|
|
OpenedPackExpansionTypes.getMemorySize() +
|
|
PackExpansionEnvironments.getMemorySize() +
|
|
size_in_bytes(PackElementExpansions) +
|
|
(DefaultedConstraints.size() * sizeof(void *)) +
|
|
nodeTypes.getMemorySize() +
|
|
keyPathComponentTypes.getMemorySize() +
|
|
size_in_bytes(KeyPaths) +
|
|
(contextualTypes.size() * sizeof(ASTNode)) +
|
|
size_in_bytes(targets) +
|
|
size_in_bytes(caseLabelItems) +
|
|
size_in_bytes(exprPatterns) +
|
|
size_in_bytes(isolatedParams) +
|
|
size_in_bytes(preconcurrencyClosures) +
|
|
size_in_bytes(resultBuilderTransformed) +
|
|
size_in_bytes(appliedPropertyWrappers) +
|
|
size_in_bytes(argumentLists) +
|
|
size_in_bytes(ImplicitCallAsFunctionRoots) +
|
|
size_in_bytes(SynthesizedConformances);
|
|
// clang-format on
|
|
|
|
return *TotalMemory;
|
|
}
|
|
|
|
DeclContext *Solution::getDC() const { return constraintSystem->DC; }
|
|
|
|
DeclName OverloadChoice::getName() const {
|
|
switch (getKind()) {
|
|
case OverloadChoiceKind::Decl:
|
|
case OverloadChoiceKind::DeclViaDynamic:
|
|
case OverloadChoiceKind::DeclViaBridge:
|
|
case OverloadChoiceKind::DeclViaUnwrappedOptional:
|
|
return getDecl()->getName();
|
|
|
|
case OverloadChoiceKind::KeyPathApplication:
|
|
// TODO: This should probably produce subscript(keyPath:), but we
|
|
// don't currently pre-filter subscript overload sets by argument
|
|
// keywords, so "subscript" is still the name that keypath subscripts
|
|
// are looked up by.
|
|
return DeclBaseName::createSubscript();
|
|
|
|
case OverloadChoiceKind::DynamicMemberLookup:
|
|
case OverloadChoiceKind::KeyPathDynamicMemberLookup:
|
|
return DeclName(DynamicMember.getPointer());
|
|
|
|
case OverloadChoiceKind::MaterializePack:
|
|
case OverloadChoiceKind::TupleIndex:
|
|
case OverloadChoiceKind::ExtractFunctionIsolation:
|
|
return DeclName();
|
|
}
|
|
|
|
llvm_unreachable("Unhandled OverloadChoiceKind in switch.");
|
|
}
|
|
|
|
std::optional<IUOReferenceKind>
|
|
OverloadChoice::getIUOReferenceKind(ConstraintSystem &cs,
|
|
bool forSecondApplication) const {
|
|
auto *decl = getDeclOrNull();
|
|
if (!decl || !decl->isImplicitlyUnwrappedOptional())
|
|
return std::nullopt;
|
|
|
|
// If this isn't an IUO return () -> T!, it's an IUO value.
|
|
if (!decl->getInterfaceType()->is<AnyFunctionType>())
|
|
return IUOReferenceKind::Value;
|
|
|
|
auto refKind = getFunctionRefInfo();
|
|
assert(!forSecondApplication || refKind.isDoubleApply());
|
|
|
|
switch (refKind.getApplyLevel()) {
|
|
case FunctionRefInfo::ApplyLevel::Unapplied:
|
|
// Such references never produce IUOs.
|
|
return std::nullopt;
|
|
case FunctionRefInfo::ApplyLevel::SingleApply:
|
|
case FunctionRefInfo::ApplyLevel::DoubleApply:
|
|
// Check whether this is a curried function reference e.g
|
|
// (Self) -> (Args...) -> Ret. Such a function reference can only produce
|
|
// an IUO on the second application.
|
|
auto isCurried = decl->hasCurriedSelf() && !hasAppliedSelf(cs, *this);
|
|
if (forSecondApplication != isCurried)
|
|
return std::nullopt;
|
|
break;
|
|
}
|
|
return IUOReferenceKind::ReturnValue;
|
|
}
|
|
|
|
SolutionResult ConstraintSystem::salvage() {
|
|
if (isDebugMode()) {
|
|
llvm::errs() << "---Attempting to salvage and emit diagnostics---\n";
|
|
}
|
|
|
|
setPhase(ConstraintSystemPhase::Diagnostics);
|
|
|
|
// Attempt to solve again, capturing all states that come from our attempts to
|
|
// select overloads or bind type variables.
|
|
//
|
|
// FIXME: can this be removed? We need to arrange for recordFixes to be
|
|
// eliminated.
|
|
SmallVector<Solution, 2> viable;
|
|
viable.clear();
|
|
|
|
{
|
|
// Set up solver state.
|
|
SolverState state(*this, FreeTypeVariableBinding::Disallow);
|
|
state.recordFixes = true;
|
|
|
|
// Solve the system.
|
|
solveImpl(viable);
|
|
|
|
// If we hit a threshold, we're done.
|
|
if (isTooComplex(viable))
|
|
return SolutionResult::forTooComplex(getTooComplexRange());
|
|
|
|
// Before removing any "fixed" solutions, let's check
|
|
// if ambiguity is caused by fixes and diagnose if possible.
|
|
if (diagnoseAmbiguityWithFixes(viable))
|
|
return SolutionResult::forAmbiguous(viable);
|
|
|
|
// Check whether we have a best solution; this can happen if we found
|
|
// a series of fixes that worked.
|
|
if (auto best = findBestSolution(viable, /*minimize=*/true)) {
|
|
if (*best != 0)
|
|
viable[0] = std::move(viable[*best]);
|
|
viable.erase(viable.begin() + 1, viable.end());
|
|
return SolutionResult::forSolved(std::move(viable[0]));
|
|
}
|
|
|
|
if (shouldSuppressDiagnostics())
|
|
return viable.empty() ? SolutionResult::forUndiagnosedError()
|
|
: SolutionResult::forAmbiguous(viable);
|
|
|
|
// FIXME: If we were able to actually fix things along the way,
|
|
// we may have to hunt for the best solution. For now, we don't care.
|
|
|
|
// Remove solutions that require fixes; the fixes in those systems should
|
|
// be diagnosed rather than any ambiguity.
|
|
auto hasFixes = [](const Solution &sol) { return !sol.Fixes.empty(); };
|
|
auto newEnd = std::remove_if(viable.begin(), viable.end(), hasFixes);
|
|
viable.erase(newEnd, viable.end());
|
|
|
|
// If there are multiple solutions, try to diagnose an ambiguity.
|
|
if (viable.size() > 1) {
|
|
if (isDebugMode()) {
|
|
auto &log = llvm::errs();
|
|
log << "---Ambiguity error: " << viable.size()
|
|
<< " solutions found---\n";
|
|
int i = 0;
|
|
for (auto &solution : viable) {
|
|
log << "---Ambiguous solution #" << i++ << "---\n";
|
|
solution.dump(log, solverState->getCurrentIndent());
|
|
log << "\n";
|
|
}
|
|
}
|
|
|
|
if (diagnoseAmbiguity(viable)) {
|
|
return SolutionResult::forAmbiguous(viable);
|
|
}
|
|
}
|
|
|
|
// Fall through to produce diagnostics.
|
|
}
|
|
|
|
// Could not produce a specific diagnostic; punt to the client.
|
|
return SolutionResult::forUndiagnosedError();
|
|
}
|
|
|
|
static void diagnoseOperatorAmbiguity(ConstraintSystem &cs,
|
|
Identifier operatorName,
|
|
ArrayRef<Solution> solutions,
|
|
ConstraintLocator *locator) {
|
|
auto &ctx = cs.getASTContext();
|
|
auto &DE = ctx.Diags;
|
|
auto *anchor = castToExpr(locator->getAnchor());
|
|
auto *applyExpr = cast<ApplyExpr>(cs.getParentExpr(anchor));
|
|
|
|
auto isEnumWithAssociatedValues = [](Type type) -> bool {
|
|
if (auto *enumType = type->getAs<EnumType>())
|
|
return !enumType->getDecl()->hasOnlyCasesWithoutAssociatedValues();
|
|
return false;
|
|
};
|
|
|
|
const auto &solution = solutions.front();
|
|
if (auto *binaryOp = dyn_cast<BinaryExpr>(applyExpr)) {
|
|
auto *lhs = binaryOp->getLHS();
|
|
auto *rhs = binaryOp->getRHS();
|
|
|
|
auto lhsType =
|
|
solution.simplifyType(solution.getType(lhs))->getRValueType();
|
|
auto rhsType =
|
|
solution.simplifyType(solution.getType(rhs))->getRValueType();
|
|
|
|
if (lhsType->isEqual(rhsType)) {
|
|
DE.diagnose(anchor->getLoc(), diag::cannot_apply_binop_to_same_args,
|
|
operatorName.str(), lhsType)
|
|
.highlight(lhs->getSourceRange())
|
|
.highlight(rhs->getSourceRange());
|
|
|
|
if (isStandardComparisonOperator(binaryOp->getFn()) &&
|
|
isEnumWithAssociatedValues(lhsType)) {
|
|
DE.diagnose(applyExpr->getLoc(),
|
|
diag::no_binary_op_overload_for_enum_with_payload,
|
|
operatorName.str());
|
|
return;
|
|
}
|
|
} else if (operatorName == ctx.Id_MatchOperator) {
|
|
DE.diagnose(anchor->getLoc(), diag::cannot_match_expr_pattern_with_value,
|
|
lhsType, rhsType);
|
|
} else {
|
|
DE.diagnose(anchor->getLoc(), diag::cannot_apply_binop_to_args,
|
|
operatorName.str(), lhsType, rhsType)
|
|
.highlight(lhs->getSourceRange())
|
|
.highlight(rhs->getSourceRange());
|
|
}
|
|
} else {
|
|
auto *arg = applyExpr->getArgs()->getUnlabeledUnaryExpr();
|
|
assert(arg && "Expected a unary arg");
|
|
auto argType = solution.simplifyType(solution.getType(arg));
|
|
DE.diagnose(anchor->getLoc(), diag::cannot_apply_unop_to_arg,
|
|
operatorName.str(), argType->getRValueType());
|
|
}
|
|
|
|
std::set<std::string> parameters;
|
|
for (const auto &solution : solutions) {
|
|
auto overload = solution.getOverloadChoice(locator);
|
|
auto overloadType = overload.adjustedOpenedType;
|
|
// Let's suggest only concrete overloads here.
|
|
// Notes are going to take care of the rest,
|
|
// since printing types like `(Self, Self)` is not
|
|
// really useful.
|
|
if (overloadType->hasTypeVariable())
|
|
continue;
|
|
|
|
auto overloadFnTy = overloadType->getAs<FunctionType>();
|
|
if (!overloadFnTy)
|
|
continue;
|
|
|
|
// If arguments to all parameters have been fixed then there is nothing
|
|
// to note about in this overload.
|
|
std::set<unsigned> fixedParams;
|
|
llvm::for_each(solution.Fixes, [&](const ConstraintFix *fix) {
|
|
auto *locator = fix->getLocator();
|
|
if (getAsExpr(locator->getAnchor()) != applyExpr)
|
|
return;
|
|
|
|
if (auto argLoc = locator->findLast<LocatorPathElt::ApplyArgToParam>()) {
|
|
fixedParams.insert(argLoc->getParamIdx());
|
|
}
|
|
});
|
|
|
|
if (fixedParams.size() == overloadFnTy->getNumParams())
|
|
continue;
|
|
|
|
parameters.insert(
|
|
FunctionType::getParamListAsString(overloadFnTy->getParams()));
|
|
}
|
|
|
|
// All of the overload choices had generic parameters like `Self`.
|
|
if (parameters.empty())
|
|
return;
|
|
|
|
DE.diagnose(anchor->getLoc(), diag::suggest_partial_overloads,
|
|
/*isResult=*/false, operatorName.str(),
|
|
llvm::join(parameters, ", "));
|
|
}
|
|
|
|
std::string swift::describeGenericType(ValueDecl *GP, bool includeName) {
|
|
if (!GP)
|
|
return "";
|
|
|
|
Decl *parent = nullptr;
|
|
if (auto *AT = dyn_cast<AssociatedTypeDecl>(GP)) {
|
|
parent = AT->getProtocol();
|
|
} else {
|
|
auto *dc = GP->getDeclContext();
|
|
parent = dc->getInnermostDeclarationDeclContext();
|
|
}
|
|
|
|
if (!parent)
|
|
return "";
|
|
|
|
llvm::SmallString<64> result;
|
|
llvm::raw_svector_ostream OS(result);
|
|
|
|
OS << Decl::getDescriptiveKindName(GP->getDescriptiveKind());
|
|
|
|
if (includeName && GP->hasName())
|
|
OS << " '" << GP->getBaseName() << "'";
|
|
|
|
OS << " of ";
|
|
OS << Decl::getDescriptiveKindName(parent->getDescriptiveKind());
|
|
if (auto *decl = dyn_cast<ValueDecl>(parent)) {
|
|
if (decl->hasName())
|
|
OS << " '" << decl->getName() << "'";
|
|
}
|
|
|
|
return OS.str().str();
|
|
}
|
|
|
|
/// Special handling of conflicts associated with generic arguments.
|
|
///
|
|
/// func foo<T>(_: T, _: T) {}
|
|
/// func bar(x: Int, y: Float) {
|
|
/// foo(x, y)
|
|
/// }
|
|
///
|
|
/// It's done by first retrieving all generic parameters from each solution,
|
|
/// filtering bindings into a distinct set and diagnosing any differences.
|
|
static bool diagnoseConflictingGenericArguments(ConstraintSystem &cs,
|
|
const SolutionDiff &diff,
|
|
ArrayRef<Solution> solutions) {
|
|
if (!diff.overloads.empty())
|
|
return false;
|
|
|
|
bool noFixes = llvm::all_of(solutions, [](const Solution &solution) -> bool {
|
|
const auto score = solution.getFixedScore();
|
|
return score.Data[SK_Fix] == 0 && solution.Fixes.empty();
|
|
});
|
|
|
|
bool allMismatches =
|
|
llvm::all_of(solutions, [](const Solution &solution) -> bool {
|
|
return llvm::all_of(
|
|
solution.Fixes, [](const ConstraintFix *fix) -> bool {
|
|
return fix->getKind() == FixKind::AllowArgumentTypeMismatch ||
|
|
fix->getKind() == FixKind::AllowFunctionTypeMismatch ||
|
|
fix->getKind() == FixKind::AllowTupleTypeMismatch ||
|
|
fix->getKind() == FixKind::GenericArgumentsMismatch ||
|
|
fix->getKind() == FixKind::InsertCall ||
|
|
fix->getKind() == FixKind::IgnoreCollectionElementContextualMismatch;
|
|
});
|
|
});
|
|
|
|
if (!noFixes && !allMismatches)
|
|
return false;
|
|
|
|
auto &DE = cs.getASTContext().Diags;
|
|
|
|
llvm::SmallDenseMap<TypeVariableType *,
|
|
std::pair<GenericTypeParamType *, SourceLoc>, 4>
|
|
genericParams;
|
|
// Consider all representative type variables across all solutions.
|
|
for (auto &solution : solutions) {
|
|
for (auto &typeBinding : solution.typeBindings) {
|
|
auto *typeVar = typeBinding.first;
|
|
if (auto *GP = typeVar->getImpl().getGenericParameter()) {
|
|
auto *locator = typeVar->getImpl().getLocator();
|
|
auto *repr = cs.getRepresentative(typeVar);
|
|
// If representative is another generic parameter let's
|
|
// use its generic parameter type instead of originator's,
|
|
// but it's possible that generic parameter is equated to
|
|
// some other type e.g.
|
|
//
|
|
// func foo<T>(_: T) -> T {}
|
|
//
|
|
// In this case when reference to function `foo` is "opened"
|
|
// type variable representing `T` would be equated to
|
|
// type variable representing a result type of the reference.
|
|
if (auto *reprGP = repr->getImpl().getGenericParameter())
|
|
GP = reprGP;
|
|
|
|
genericParams[repr] = {GP, getLoc(locator->getAnchor())};
|
|
}
|
|
}
|
|
}
|
|
|
|
llvm::SmallDenseMap<std::pair<GenericTypeParamType *, SourceLoc>,
|
|
SmallVector<Type, 4>>
|
|
conflicts;
|
|
|
|
for (const auto &entry : genericParams) {
|
|
auto *typeVar = entry.first;
|
|
auto GP = entry.second;
|
|
|
|
swift::SmallSetVector<Type, 4> arguments;
|
|
for (const auto &solution : solutions) {
|
|
auto type = solution.typeBindings.lookup(typeVar);
|
|
// Type variables gathered from a solution's type binding context may not
|
|
// exist in another given solution because some solutions may have
|
|
// additional type variables not present in other solutions due to taking
|
|
// different paths in the solver.
|
|
if (!type)
|
|
continue;
|
|
|
|
// Contextual opaque result type is uniquely identified by
|
|
// declaration it's associated with, so we have to compare
|
|
// declarations instead of using pointer equality on such types.
|
|
if (auto *opaque = type->getAs<OpaqueTypeArchetypeType>()) {
|
|
auto *decl = opaque->getDecl();
|
|
arguments.remove_if([&](Type argType) -> bool {
|
|
if (auto *otherOpaque = argType->getAs<OpaqueTypeArchetypeType>()) {
|
|
return decl == otherOpaque->getDecl();
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
|
|
arguments.insert(type);
|
|
}
|
|
|
|
if (arguments.size() > 1)
|
|
conflicts[GP].append(arguments.begin(), arguments.end());
|
|
}
|
|
|
|
auto getGenericTypeDecl = [&](ArchetypeType *archetype) -> ValueDecl * {
|
|
auto type = archetype->getInterfaceType();
|
|
|
|
if (auto *GTPT = type->getAs<GenericTypeParamType>())
|
|
return GTPT->getDecl();
|
|
|
|
if (auto *DMT = type->getAs<DependentMemberType>())
|
|
return DMT->getAssocType();
|
|
|
|
return nullptr;
|
|
};
|
|
|
|
bool diagnosed = false;
|
|
for (auto &conflict : conflicts) {
|
|
SourceLoc loc;
|
|
GenericTypeParamType *GP;
|
|
|
|
std::tie(GP, loc) = conflict.first;
|
|
auto conflictingArguments = conflict.second;
|
|
|
|
// If there are any substitutions that are not fully resolved
|
|
// solutions cannot be considered conflicting for the given parameter.
|
|
if (llvm::any_of(conflictingArguments,
|
|
[](const auto &arg) { return arg->hasPlaceholder(); }))
|
|
continue;
|
|
|
|
llvm::SmallString<64> arguments;
|
|
llvm::raw_svector_ostream OS(arguments);
|
|
|
|
interleave(
|
|
conflictingArguments,
|
|
[&](Type argType) {
|
|
OS << "'" << argType << "'";
|
|
|
|
if (auto *opaque = argType->getAs<OpaqueTypeArchetypeType>()) {
|
|
auto *decl = opaque->getDecl()->getNamingDecl();
|
|
OS << " (result type of '" << decl->getBaseName().userFacingName()
|
|
<< "')";
|
|
return;
|
|
}
|
|
|
|
if (auto archetype = argType->getAs<ArchetypeType>()) {
|
|
if (auto *GTD = getGenericTypeDecl(archetype))
|
|
OS << " (" << describeGenericType(GTD) << ")";
|
|
}
|
|
},
|
|
[&OS] { OS << " vs. "; });
|
|
|
|
DE.diagnose(loc, diag::conflicting_arguments_for_generic_parameter, GP,
|
|
OS.str());
|
|
diagnosed = true;
|
|
}
|
|
|
|
return diagnosed;
|
|
}
|
|
|
|
/// Diagnose ambiguity related to overloaded declarations where only
|
|
/// *some* of the overload choices have ephemeral pointer warnings/errors
|
|
/// associated with them. Such situations have be handled specifically
|
|
/// because ephemeral fixes do not affect the score.
|
|
///
|
|
/// If all of the overloads have ephemeral fixes associated with them
|
|
/// it's much easier to diagnose through notes associated with each fix.
|
|
static bool
|
|
diagnoseAmbiguityWithEphemeralPointers(ConstraintSystem &cs,
|
|
ArrayRef<Solution> solutions) {
|
|
unsigned numSolutionsWithFixes = 0;
|
|
for (const auto &solution : solutions) {
|
|
if (solution.Fixes.empty()) {
|
|
continue;
|
|
}
|
|
|
|
if (!llvm::all_of(solution.Fixes, [](const ConstraintFix *fix) {
|
|
return fix->getKind() == FixKind::TreatEphemeralAsNonEphemeral;
|
|
}))
|
|
return false;
|
|
|
|
numSolutionsWithFixes += 1;
|
|
}
|
|
|
|
// If all or no solutions have fixes for ephemeral pointers, let's
|
|
// let `diagnoseAmbiguityWithFixes` diagnose the problem.
|
|
if (numSolutionsWithFixes == 0 ||
|
|
numSolutionsWithFixes == solutions.size())
|
|
return false;
|
|
|
|
// If only some of the solutions have ephemeral pointer fixes
|
|
// let's let `diagnoseAmbiguity` diagnose the problem either
|
|
// with affected argument or related declaration e.g. function ref.
|
|
return cs.diagnoseAmbiguity(solutions);
|
|
}
|
|
|
|
static bool diagnoseAmbiguityWithContextualType(
|
|
ConstraintSystem &cs, SolutionDiff &solutionDiff,
|
|
ArrayRef<std::pair<const Solution *, const ConstraintFix *>> aggregateFix,
|
|
ArrayRef<Solution> solutions) {
|
|
// Diagnose only if contextual failure is associated with every solution.
|
|
if (aggregateFix.size() < solutions.size())
|
|
return false;
|
|
|
|
auto getResultType =
|
|
[](const std::pair<const Solution *, const ConstraintFix *> &entry)
|
|
-> Type {
|
|
auto &solution = *entry.first;
|
|
auto anchor = entry.second->getLocator()->getAnchor();
|
|
return solution.simplifyType(solution.getType(anchor));
|
|
};
|
|
|
|
auto resultType = getResultType(aggregateFix.front());
|
|
// If right-hand side of the conversion (result of the AST node)
|
|
// is the same across all of the solutions let's diagnose it as if
|
|
// it it as a single failure.
|
|
if (llvm::all_of(
|
|
aggregateFix,
|
|
[&](const std::pair<const Solution *, const ConstraintFix *> &entry) {
|
|
return resultType->isEqual(getResultType(entry));
|
|
})) {
|
|
auto &fix = aggregateFix.front();
|
|
return fix.second->diagnose(*fix.first, /*asNote=*/false);
|
|
}
|
|
|
|
// If result types are different it could only mean that this is an attempt
|
|
// to convert a reference to, or call of overloaded declaration to a
|
|
// particular type.
|
|
|
|
auto &solution = *aggregateFix.front().first;
|
|
auto *locator = aggregateFix.front().second->getLocator();
|
|
auto *calleeLocator = solution.getCalleeLocator(locator);
|
|
|
|
auto result =
|
|
llvm::find_if(solutionDiff.overloads,
|
|
[&calleeLocator](const SolutionDiff::OverloadDiff &entry) {
|
|
return entry.locator == calleeLocator;
|
|
});
|
|
|
|
if (result == solutionDiff.overloads.end())
|
|
return false;
|
|
|
|
auto &DE = cs.getASTContext().Diags;
|
|
|
|
auto anchor = locator->getAnchor();
|
|
auto name = result->choices.front().getName();
|
|
auto contextualTy = solution.getContextualType(anchor);
|
|
|
|
// In some situations `getContextualType` for a contextual type
|
|
// locator is going to return then empty type. This happens because
|
|
// e.g. optional-some patterns and patterns with incorrect type don't
|
|
// have a contextual type for initialization expression but use
|
|
// a conversion with contextual locator nevertheless to indicate
|
|
// the purpose. This doesn't affect non-ambiguity diagnostics
|
|
// because mismatches carry both `from` and `to` types.
|
|
if (!contextualTy)
|
|
return false;
|
|
|
|
DE.diagnose(getLoc(anchor),
|
|
contextualTy->is<ProtocolType>()
|
|
? diag::no_overloads_have_result_type_conformance
|
|
: diag::no_candidates_match_result_type,
|
|
name.getBaseName().userFacingName(), contextualTy);
|
|
|
|
for (const auto &solution : solutions) {
|
|
auto overload = solution.getOverloadChoice(calleeLocator);
|
|
if (auto *decl = overload.choice.getDeclOrNull()) {
|
|
auto type = solution.simplifyType(overload.boundType);
|
|
|
|
if (isExpr<ApplyExpr>(anchor) || isExpr<SubscriptExpr>(anchor)) {
|
|
auto fnType = type->castTo<FunctionType>();
|
|
DE.diagnose(
|
|
decl,
|
|
contextualTy->is<ProtocolType>()
|
|
? diag::overload_result_type_does_not_conform
|
|
: diag::cannot_convert_candidate_result_to_contextual_type,
|
|
decl, fnType->getResult(), contextualTy);
|
|
} else {
|
|
DE.diagnose(decl, diag::found_candidate_type, type);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Diagnose problems with generic requirement fixes that are anchored on
|
|
/// one callee location. The list could contain different kinds of fixes
|
|
/// i.e. missing protocol conformances at different positions,
|
|
/// same-type requirement mismatches, etc.
|
|
static bool diagnoseAmbiguityWithGenericRequirements(
|
|
ConstraintSystem &cs,
|
|
ArrayRef<std::pair<const Solution *, const ConstraintFix *>> aggregate) {
|
|
// If all of the fixes point to the same overload choice,
|
|
// we can diagnose this an a single error.
|
|
bool hasNonDeclOverloads = false;
|
|
|
|
llvm::SmallSet<ValueDecl *, 4> overloadChoices;
|
|
for (const auto &entry : aggregate) {
|
|
const auto &solution = *entry.first;
|
|
auto *calleeLocator = solution.getCalleeLocator(entry.second->getLocator());
|
|
|
|
if (auto overload = solution.getOverloadChoiceIfAvailable(calleeLocator)) {
|
|
if (auto *D = overload->choice.getDeclOrNull()) {
|
|
overloadChoices.insert(D);
|
|
} else {
|
|
hasNonDeclOverloads = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto &primaryFix = aggregate.front();
|
|
{
|
|
if (overloadChoices.size() > 0) {
|
|
// Some of the choices are non-declaration,
|
|
// let's delegate that to ambiguity diagnostics.
|
|
if (hasNonDeclOverloads)
|
|
return false;
|
|
|
|
if (overloadChoices.size() == 1)
|
|
return primaryFix.second->diagnose(*primaryFix.first);
|
|
|
|
// fall through to the tailored ambiguity diagnostic.
|
|
} else {
|
|
// If there are no overload choices it means that
|
|
// the issue is with types, delegate that to the primary fix.
|
|
return primaryFix.second->diagnoseForAmbiguity(aggregate);
|
|
}
|
|
}
|
|
|
|
// Produce "no exact matches" diagnostic.
|
|
auto &ctx = cs.getASTContext();
|
|
auto *choice = *overloadChoices.begin();
|
|
|
|
ctx.Diags.diagnose(getLoc(primaryFix.second->getLocator()->getAnchor()),
|
|
diag::no_overloads_match_exactly_in_call,
|
|
/*isApplication=*/false, choice,
|
|
choice->getName().isSpecial());
|
|
|
|
for (const auto &entry : aggregate) {
|
|
entry.second->diagnose(*entry.first, /*asNote=*/true);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool diagnoseAmbiguity(
|
|
ConstraintSystem &cs, const SolutionDiff::OverloadDiff &ambiguity,
|
|
ArrayRef<std::pair<const Solution *, const ConstraintFix *>> aggregateFix,
|
|
ArrayRef<Solution> solutions) {
|
|
auto *locator = aggregateFix.front().second->getLocator();
|
|
auto anchor = aggregateFix.front().second->getAnchor();
|
|
|
|
auto &DE = cs.getASTContext().Diags;
|
|
|
|
llvm::SmallPtrSet<ValueDecl *, 4> localAmbiguity;
|
|
{
|
|
for (auto &entry : aggregateFix) {
|
|
const auto &solution = entry.first;
|
|
const auto &overload = solution->getOverloadChoice(ambiguity.locator);
|
|
auto *choice = overload.choice.getDeclOrNull();
|
|
|
|
// It's not possible to diagnose different kinds of overload choices.
|
|
if (!choice)
|
|
return false;
|
|
|
|
localAmbiguity.insert(choice);
|
|
}
|
|
}
|
|
|
|
if (localAmbiguity.empty())
|
|
return false;
|
|
|
|
// If all of the fixes are rooted in the same choice.
|
|
if (localAmbiguity.size() == 1) {
|
|
auto &primaryFix = aggregateFix.front();
|
|
return primaryFix.second->diagnose(*primaryFix.first);
|
|
}
|
|
|
|
{
|
|
auto fixKind = aggregateFix.front().second->getKind();
|
|
if (llvm::all_of(
|
|
aggregateFix, [&](const std::pair<const Solution *,
|
|
const ConstraintFix *> &entry) {
|
|
auto &fix = entry.second;
|
|
return fix->getKind() == fixKind && fix->getLocator() == locator;
|
|
})) {
|
|
auto *primaryFix = aggregateFix.front().second;
|
|
if (primaryFix->diagnoseForAmbiguity(aggregateFix))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
auto *decl = *localAmbiguity.begin();
|
|
auto *commonCalleeLocator = ambiguity.locator;
|
|
|
|
bool diagnosed = true;
|
|
{
|
|
DiagnosticTransaction transaction(DE);
|
|
|
|
auto commonAnchor = commonCalleeLocator->getAnchor();
|
|
if (auto *callExpr = getAsExpr<CallExpr>(commonAnchor))
|
|
commonAnchor = callExpr->getDirectCallee();
|
|
|
|
const auto name = decl->getName();
|
|
|
|
// Emit an error message for the ambiguity.
|
|
if (locator->isForContextualType()) {
|
|
auto baseName = name.getBaseName();
|
|
DE.diagnose(getLoc(commonAnchor), diag::no_candidates_match_result_type,
|
|
baseName.userFacingName(),
|
|
cs.getContextualType(anchor, /*forConstraint=*/false));
|
|
} else if (name.isOperator()) {
|
|
auto *anchor = castToExpr(commonCalleeLocator->getAnchor());
|
|
|
|
// If operator is "applied" e.g. `1 + 2` there are tailored
|
|
// diagnostics in case of ambiguity, but if it's referenced
|
|
// e.g. `arr.sort(by: <)` it's better to produce generic error
|
|
// and a note per candidate.
|
|
if (auto *parentExpr = cs.getParentExpr(anchor)) {
|
|
if (auto *apply = dyn_cast<ApplyExpr>(parentExpr)) {
|
|
if (apply->getFn() == anchor) {
|
|
diagnoseOperatorAmbiguity(cs, name.getBaseIdentifier(), solutions,
|
|
commonCalleeLocator);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
DE.diagnose(anchor->getLoc(), diag::no_overloads_match_exactly_in_call,
|
|
/*isApplication=*/false, decl, name.isSpecial());
|
|
} else {
|
|
bool isApplication = llvm::any_of(solutions, [&](const auto &S) {
|
|
return llvm::any_of(S.argumentLists, [&](const auto &pair) {
|
|
return pair.first->getAnchor() == commonAnchor;
|
|
});
|
|
});
|
|
|
|
DE.diagnose(getLoc(commonAnchor),
|
|
diag::no_overloads_match_exactly_in_call, isApplication,
|
|
decl, name.isSpecial());
|
|
}
|
|
|
|
// Produce candidate notes
|
|
SmallPtrSet<ValueDecl *, 4> distinctChoices;
|
|
llvm::SmallSet<CanType, 4> candidateTypes;
|
|
for (const auto &solution : solutions) {
|
|
auto overload = solution.getOverloadChoice(commonCalleeLocator);
|
|
auto *decl = overload.choice.getDecl();
|
|
auto type = solution.simplifyType(overload.adjustedOpenedType);
|
|
// Skip if we've already produced a note for this overload
|
|
if (!distinctChoices.insert(decl).second)
|
|
continue;
|
|
|
|
auto noteLoc =
|
|
decl->getLoc().isInvalid() ? getLoc(commonAnchor) : decl->getLoc();
|
|
|
|
SmallVector<const ConstraintFix *, 4> fixes;
|
|
for (const auto &entry : aggregateFix) {
|
|
if (entry.first == &solution)
|
|
fixes.push_back(entry.second);
|
|
}
|
|
|
|
auto emitGeneralFoundCandidateNote = [&]() {
|
|
// Emit a general "found candidate" note
|
|
if (decl->getLoc().isInvalid()) {
|
|
if (candidateTypes.insert(type->getCanonicalType()).second)
|
|
DE.diagnose(getLoc(commonAnchor), diag::found_candidate_type, type);
|
|
} else {
|
|
DE.diagnose(noteLoc, diag::found_candidate);
|
|
}
|
|
};
|
|
|
|
if (fixes.size() == 1) {
|
|
diagnosed &= fixes.front()->diagnose(solution, /*asNote*/ true);
|
|
} else if (!fixes.empty() &&
|
|
llvm::all_of(fixes, [&](const ConstraintFix *fix) {
|
|
// Ignore coercion fixes in this context, to
|
|
// focus on the argument mismatches.
|
|
if (fix->getLocator()->isForCoercion())
|
|
return true;
|
|
|
|
return fix->getLocator()
|
|
->findLast<LocatorPathElt::ApplyArgument>()
|
|
.has_value();
|
|
})) {
|
|
// All fixes have to do with arguments, so let's show the parameter
|
|
// lists.
|
|
//
|
|
// It's possible that function type is wrapped in an optional
|
|
// if it's from `@objc optional` method, so we need to ignore that.
|
|
auto *fn =
|
|
type->lookThroughAllOptionalTypes()->getAs<AnyFunctionType>();
|
|
assert(fn);
|
|
|
|
auto first = llvm::find_if(fixes, [&](const ConstraintFix *fix) {
|
|
return fix->getLocator()
|
|
->findLast<LocatorPathElt::ApplyArgument>()
|
|
.has_value();
|
|
});
|
|
|
|
if (first != fixes.end()) {
|
|
auto *argList = solution.getArgumentList((*first)->getLocator());
|
|
assert(argList);
|
|
|
|
if (fn->getNumParams() == 1 && argList->isUnary()) {
|
|
const auto ¶m = fn->getParams()[0];
|
|
auto argTy = solution.getResolvedType(argList->getUnaryExpr());
|
|
|
|
DE.diagnose(noteLoc,
|
|
diag::candidate_has_invalid_argument_at_position,
|
|
solution.simplifyType(param.getPlainType()),
|
|
/*position=*/1, param.isInOut(), argTy);
|
|
} else {
|
|
DE.diagnose(noteLoc, diag::candidate_partial_match,
|
|
fn->getParamListAsString(fn->getParams()));
|
|
}
|
|
} else {
|
|
// Only coercion ambiguity fixes.
|
|
emitGeneralFoundCandidateNote();
|
|
}
|
|
} else {
|
|
emitGeneralFoundCandidateNote();
|
|
}
|
|
}
|
|
|
|
// If not all of the fixes produced a note, we can't diagnose this.
|
|
if (!diagnosed)
|
|
transaction.abort();
|
|
}
|
|
|
|
return diagnosed;
|
|
}
|
|
|
|
using FixInContext = std::pair<const Solution *, const ConstraintFix *>;
|
|
|
|
// Attempts to diagnose function call ambiguities of types inferred for a result
|
|
// generic parameter from contextual type and a closure argument that
|
|
// conflicting infer a different type for the same argument. Example:
|
|
// func callit<T>(_ f: () -> T) -> T {
|
|
// f()
|
|
// }
|
|
//
|
|
// func context() -> Int {
|
|
// callit {
|
|
// print("hello")
|
|
// }
|
|
// }
|
|
// Where generic argument `T` can be inferred both as `Int` from contextual
|
|
// result and `Void` from the closure argument result.
|
|
static bool diagnoseContextualFunctionCallGenericAmbiguity(
|
|
ConstraintSystem &cs, ArrayRef<FixInContext> contextualFixes,
|
|
ArrayRef<FixInContext> allFixes) {
|
|
|
|
if (contextualFixes.empty())
|
|
return false;
|
|
|
|
auto contextualFix = contextualFixes.front();
|
|
if (!std::all_of(contextualFixes.begin() + 1, contextualFixes.end(),
|
|
[&contextualFix](FixInContext fix) {
|
|
return fix.second->getLocator() ==
|
|
contextualFix.second->getLocator();
|
|
}))
|
|
return false;
|
|
|
|
auto fixLocator = contextualFix.second->getLocator();
|
|
auto contextualAnchor = fixLocator->getAnchor();
|
|
auto *AE = getAsExpr<ApplyExpr>(contextualAnchor);
|
|
// All contextual failures anchored on the same function call.
|
|
if (!AE)
|
|
return false;
|
|
|
|
auto fnLocator = cs.getConstraintLocator(AE->getSemanticFn());
|
|
auto overload = contextualFix.first->getOverloadChoiceIfAvailable(fnLocator);
|
|
if (!overload)
|
|
return false;
|
|
|
|
auto applyFnType = overload->adjustedOpenedType->castTo<FunctionType>();
|
|
auto resultTypeVar = applyFnType->getResult()->getAs<TypeVariableType>();
|
|
if (!resultTypeVar)
|
|
return false;
|
|
|
|
auto *GP = resultTypeVar->getImpl().getGenericParameter();
|
|
if (!GP)
|
|
return false;
|
|
|
|
auto applyLoc =
|
|
cs.getConstraintLocator(AE, {LocatorPathElt::ApplyArgument()});
|
|
auto argMatching =
|
|
contextualFix.first->argumentMatchingChoices.find(applyLoc);
|
|
if (argMatching == contextualFix.first->argumentMatchingChoices.end()) {
|
|
return false;
|
|
}
|
|
|
|
auto *args = AE->getArgs();
|
|
llvm::SmallVector<ClosureExpr *, 2> closureArguments;
|
|
for (auto i : indices(*args)) {
|
|
auto *closure = getAsExpr<ClosureExpr>(args->getExpr(i));
|
|
if (!closure)
|
|
continue;
|
|
|
|
auto argParamMatch = argMatching->second.parameterBindings[i];
|
|
auto param = applyFnType->getParams()[argParamMatch.front()];
|
|
auto paramFnType = param.getPlainType()->getAs<FunctionType>();
|
|
if (!paramFnType)
|
|
continue;
|
|
|
|
if (cs.typeVarOccursInType(resultTypeVar, paramFnType->getResult()))
|
|
closureArguments.push_back(closure);
|
|
}
|
|
|
|
// If no closure result's involves the generic parameter, just bail because we
|
|
// won't find a conflict.
|
|
if (closureArguments.empty())
|
|
return false;
|
|
|
|
// At least one closure where result type involves the generic parameter.
|
|
// So let's try to collect the set of fixed types for the generic parameter
|
|
// from all the closure contextual fix/solutions and if there are more than
|
|
// one fixed type diagnose it.
|
|
swift::SmallSetVector<Type, 4> genericParamInferredTypes;
|
|
for (auto &fix : contextualFixes)
|
|
genericParamInferredTypes.insert(fix.first->getFixedType(resultTypeVar));
|
|
|
|
if (llvm::all_of(allFixes, [&](FixInContext fix) {
|
|
auto fixLocator = fix.second->getLocator();
|
|
if (fixLocator->isForContextualType())
|
|
return true;
|
|
|
|
if (!(fix.second->getKind() == FixKind::IgnoreContextualType ||
|
|
fix.second->getKind() == FixKind::AllowTupleTypeMismatch))
|
|
return false;
|
|
|
|
auto anchor = fixLocator->getAnchor();
|
|
if (!(anchor == contextualAnchor ||
|
|
fixLocator->isLastElement<LocatorPathElt::ClosureResult>() ||
|
|
fixLocator->isLastElement<LocatorPathElt::ClosureBody>()))
|
|
return false;
|
|
|
|
genericParamInferredTypes.insert(
|
|
fix.first->getFixedType(resultTypeVar));
|
|
return true;
|
|
})) {
|
|
|
|
if (genericParamInferredTypes.size() != 2)
|
|
return false;
|
|
|
|
auto &DE = cs.getASTContext().Diags;
|
|
llvm::SmallString<64> arguments;
|
|
llvm::raw_svector_ostream OS(arguments);
|
|
interleave(
|
|
genericParamInferredTypes,
|
|
[&](Type argType) { OS << "'" << argType << "'"; },
|
|
[&OS] { OS << " vs. "; });
|
|
|
|
DE.diagnose(AE->getLoc(), diag::conflicting_arguments_for_generic_parameter,
|
|
GP, OS.str());
|
|
|
|
DE.diagnose(AE->getLoc(),
|
|
diag::generic_parameter_inferred_from_result_context, GP,
|
|
genericParamInferredTypes.back());
|
|
DE.diagnose(closureArguments.front()->getStartLoc(),
|
|
diag::generic_parameter_inferred_from_closure, GP,
|
|
genericParamInferredTypes.front());
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ConstraintSystem::diagnoseAmbiguityWithFixes(
|
|
SmallVectorImpl<Solution> &solutions) {
|
|
if (solutions.empty() || shouldSuppressDiagnostics())
|
|
return false;
|
|
|
|
SolutionDiff solutionDiff(solutions);
|
|
|
|
if (diagnoseConflictingGenericArguments(*this, solutionDiff, solutions))
|
|
return true;
|
|
|
|
if (auto bestScore = solverState->BestScore) {
|
|
solutions.erase(llvm::remove_if(solutions,
|
|
[&](const Solution &solution) {
|
|
return solution.getFixedScore() >
|
|
*bestScore;
|
|
}),
|
|
solutions.end());
|
|
|
|
if (llvm::all_of(solutions, [&](const Solution &solution) {
|
|
auto score = solution.getFixedScore();
|
|
return score.Data[SK_Fix] == 0 && solution.Fixes.empty();
|
|
}))
|
|
return false;
|
|
}
|
|
|
|
if (solutions.size() < 2)
|
|
return false;
|
|
|
|
if (diagnoseAmbiguityWithEphemeralPointers(*this, solutions))
|
|
return true;
|
|
|
|
if (isDebugMode()) {
|
|
auto indent = solverState->getCurrentIndent();
|
|
auto &log = llvm::errs().indent(indent);
|
|
log << "--- Ambiguity: Considering #" << solutions.size()
|
|
<< " solutions with fixes ---\n";
|
|
int i = 0;
|
|
for (auto &solution : solutions) {
|
|
log << "\n";
|
|
log.indent(indent) << "--- Solution #" << i++ << "---\n";
|
|
solution.dump(log, indent);
|
|
log << "\n";
|
|
}
|
|
}
|
|
|
|
// If there either no fixes at all or all of the are warnings,
|
|
// let's diagnose this as regular ambiguity.
|
|
if (llvm::all_of(solutions, [](const Solution &solution) {
|
|
return llvm::all_of(solution.Fixes, [](const ConstraintFix *fix) {
|
|
return !fix->isFatal();
|
|
});
|
|
})) {
|
|
return diagnoseAmbiguity(solutions);
|
|
}
|
|
|
|
// Algorithm is as follows:
|
|
//
|
|
// a. Aggregate all of the available fixes based on callee locator;
|
|
// b. For each ambiguous overload match aggregated fixes and diagnose;
|
|
// c. Discard all of the fixes which have been already considered
|
|
// as part of overload diagnostics;
|
|
// d. Diagnose remaining (uniqued based on kind + locator) fixes
|
|
// iff they appear in all of the solutions.
|
|
|
|
llvm::SmallSetVector<FixInContext, 4> fixes;
|
|
for (auto &solution : solutions) {
|
|
for (auto *fix : solution.Fixes) {
|
|
// If the fix doesn't affect the solution score, it is not the
|
|
// source of ambiguity or failures.
|
|
// Ignore warnings in favor of actual error fixes,
|
|
// because they are not the source of ambiguity/failures.
|
|
if (!fix->impact())
|
|
continue;
|
|
|
|
fixes.insert({&solution, fix});
|
|
}
|
|
}
|
|
|
|
llvm::MapVector<ConstraintLocator *, SmallVector<FixInContext, 4>>
|
|
fixesByCallee;
|
|
llvm::SmallVector<FixInContext, 4> contextualFixes;
|
|
|
|
for (const auto &entry : fixes) {
|
|
const auto &solution = *entry.first;
|
|
const auto *fix = entry.second;
|
|
|
|
auto *locator = fix->getLocator();
|
|
|
|
if (locator->isForContextualType()) {
|
|
contextualFixes.push_back({&solution, fix});
|
|
continue;
|
|
}
|
|
|
|
auto *calleeLocator = solution.getCalleeLocator(locator);
|
|
fixesByCallee[calleeLocator].push_back({&solution, fix});
|
|
}
|
|
|
|
bool diagnosed = false;
|
|
|
|
// All of the fixes which have been considered already.
|
|
llvm::SmallSetVector<FixInContext, 4> consideredFixes;
|
|
|
|
for (const auto &ambiguity : solutionDiff.overloads) {
|
|
auto fixes = fixesByCallee.find(ambiguity.locator);
|
|
if (fixes == fixesByCallee.end())
|
|
continue;
|
|
|
|
auto aggregate = fixes->second;
|
|
diagnosed |= ::diagnoseAmbiguity(*this, ambiguity, aggregate, solutions);
|
|
|
|
consideredFixes.insert(aggregate.begin(), aggregate.end());
|
|
}
|
|
|
|
if (diagnoseAmbiguityWithContextualType(*this, solutionDiff, contextualFixes,
|
|
solutions)) {
|
|
consideredFixes.insert(contextualFixes.begin(), contextualFixes.end());
|
|
diagnosed |= true;
|
|
}
|
|
|
|
// Remove all of the fixes which have been attached to ambiguous
|
|
// overload choices.
|
|
fixes.set_subtract(consideredFixes);
|
|
|
|
// Aggregate all requirement fixes that belong to the same callee
|
|
// and attempt to diagnose possible ambiguities.
|
|
{
|
|
// Aggregates fixes fixes attached to `buildExpression` and `buildBlock`
|
|
// methods at the particular source location.
|
|
llvm::MapVector<SourceLoc, SmallVector<FixInContext, 4>>
|
|
builderMethodRequirementFixes;
|
|
|
|
llvm::MapVector<ConstraintLocator *, SmallVector<FixInContext, 4>>
|
|
perCalleeRequirementFixes;
|
|
|
|
for (const auto &entry : fixes) {
|
|
auto *fix = entry.second;
|
|
if (!fix->getLocator()->isLastElement<LocatorPathElt::AnyRequirement>())
|
|
continue;
|
|
|
|
auto *calleeLoc = entry.first->getCalleeLocator(fix->getLocator());
|
|
|
|
auto *UDE = getAsExpr<UnresolvedDotExpr>(calleeLoc->getAnchor());
|
|
if (UDE && isResultBuilderMethodReference(getASTContext(), UDE)) {
|
|
auto *anchor = castToExpr<Expr>(calleeLoc->getAnchor());
|
|
builderMethodRequirementFixes[anchor->getLoc()].push_back(entry);
|
|
} else {
|
|
perCalleeRequirementFixes[calleeLoc].push_back(entry);
|
|
}
|
|
}
|
|
|
|
SmallVector<SmallVector<FixInContext, 4>, 4> viableGroups;
|
|
{
|
|
auto takeAggregateIfViable =
|
|
[&](SmallVector<FixInContext, 4> &aggregate) {
|
|
// Ambiguity only if all of the solutions have a requirement
|
|
// fix at the given location.
|
|
if (aggregate.size() == solutions.size())
|
|
viableGroups.push_back(std::move(aggregate));
|
|
};
|
|
|
|
for (auto &entry : builderMethodRequirementFixes)
|
|
takeAggregateIfViable(entry.second);
|
|
|
|
for (auto &entry : perCalleeRequirementFixes)
|
|
takeAggregateIfViable(entry.second);
|
|
}
|
|
|
|
for (auto &aggregate : viableGroups) {
|
|
if (diagnoseAmbiguityWithGenericRequirements(*this, aggregate)) {
|
|
// Remove diagnosed fixes.
|
|
fixes.set_subtract(aggregate);
|
|
diagnosed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
llvm::MapVector<std::pair<FixKind, ConstraintLocator *>,
|
|
SmallVector<FixInContext, 4>>
|
|
fixesByKind;
|
|
|
|
for (const auto &entry : fixes) {
|
|
const auto *fix = entry.second;
|
|
fixesByKind[{fix->getKind(), fix->getLocator()}].push_back(entry);
|
|
}
|
|
|
|
// If leftover fix is contained in all of the solutions let's
|
|
// diagnose it as ambiguity.
|
|
for (const auto &entry : fixesByKind) {
|
|
if (llvm::all_of(solutions, [&](const Solution &solution) -> bool {
|
|
return llvm::any_of(
|
|
solution.Fixes, [&](const ConstraintFix *fix) -> bool {
|
|
return std::make_pair(fix->getKind(), fix->getLocator()) ==
|
|
entry.first;
|
|
});
|
|
})) {
|
|
auto &aggregate = entry.second;
|
|
diagnosed |= aggregate.front().second->diagnoseForAmbiguity(aggregate);
|
|
}
|
|
}
|
|
|
|
if (!diagnosed && diagnoseContextualFunctionCallGenericAmbiguity(
|
|
*this, contextualFixes, fixes.getArrayRef()))
|
|
return true;
|
|
|
|
return diagnosed;
|
|
}
|
|
|
|
/// Determine the number of distinct overload choices in the
|
|
/// provided set.
|
|
static unsigned countDistinctOverloads(ArrayRef<OverloadChoice> choices) {
|
|
llvm::SmallPtrSet<void *, 4> uniqueChoices;
|
|
for (auto choice : choices) {
|
|
uniqueChoices.insert(choice.getOpaqueChoiceSimple());
|
|
}
|
|
return uniqueChoices.size();
|
|
}
|
|
|
|
static Type getOverloadChoiceType(ConstraintLocator *overloadLoc,
|
|
const Solution &solution) {
|
|
auto selectedOverload = solution.overloadChoices.find(overloadLoc);
|
|
if (selectedOverload == solution.overloadChoices.end())
|
|
return Type();
|
|
return solution.simplifyType(selectedOverload->second.adjustedOpenedType);
|
|
}
|
|
|
|
/// Determine the name of the overload in a set of overload choices.
|
|
static DeclName getOverloadChoiceName(ArrayRef<OverloadChoice> choices) {
|
|
DeclName name;
|
|
for (auto choice : choices) {
|
|
if (!choice.isDecl())
|
|
continue;
|
|
|
|
const DeclName nextName = choice.getDecl()->getName();
|
|
if (!name) {
|
|
name = nextName;
|
|
continue;
|
|
}
|
|
|
|
if (name != nextName) {
|
|
// Assume all choices have the same base name and only differ in
|
|
// argument labels. This may not be a great assumption, but we don't
|
|
// really have a way to recover for diagnostics otherwise.
|
|
return name.getBaseName();
|
|
}
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
/// Extend the given index map with all of the subexpressions in the given
|
|
/// expression.
|
|
static void extendPreorderIndexMap(
|
|
Expr *expr, llvm::DenseMap<Expr *, unsigned> &indexMap) {
|
|
class RecordingTraversal : public ASTWalker {
|
|
public:
|
|
llvm::DenseMap<Expr *, unsigned> &IndexMap;
|
|
unsigned Index = 0;
|
|
|
|
explicit RecordingTraversal(llvm::DenseMap<Expr *, unsigned> &indexMap)
|
|
: IndexMap(indexMap) { }
|
|
|
|
MacroWalking getMacroWalkingBehavior() const override {
|
|
return MacroWalking::ArgumentsAndExpansion;
|
|
}
|
|
|
|
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
|
|
IndexMap[E] = Index;
|
|
++Index;
|
|
return Action::Continue(E);
|
|
}
|
|
};
|
|
|
|
RecordingTraversal traversal(indexMap);
|
|
expr->walk(traversal);
|
|
}
|
|
|
|
bool ConstraintSystem::diagnoseAmbiguity(ArrayRef<Solution> solutions) {
|
|
// Produce a diff of the solutions.
|
|
SolutionDiff diff(solutions);
|
|
|
|
// Find the locators which have the largest numbers of distinct overloads.
|
|
std::optional<unsigned> bestOverload;
|
|
// Overloads are scored by lexicographical comparison of (# of distinct
|
|
// overloads, depth, *reverse* of the index). N.B. - cannot be used for the
|
|
// reversing: the score version of index == 0 should be > than that of 1, but
|
|
// -0 == 0 < UINT_MAX == -1, whereas ~0 == UINT_MAX > UINT_MAX - 1 == ~1.
|
|
auto score = [](unsigned depth, unsigned index, unsigned distinctOverloads) {
|
|
return std::make_tuple(depth, ~index, distinctOverloads);
|
|
};
|
|
auto bestScore = score(0, std::numeric_limits<unsigned>::max(), 0);
|
|
|
|
// Get a map of expressions to their depths and post-order traversal indices.
|
|
// Heuristically, all other things being equal, we should complain about the
|
|
// ambiguous expression that (1) is deepest, (2) comes earliest in the
|
|
// expression, or (3) has the most overloads.
|
|
llvm::DenseMap<Expr *, unsigned> indexMap;
|
|
for (auto expr : InputExprs) {
|
|
extendPreorderIndexMap(expr, indexMap);
|
|
}
|
|
|
|
for (unsigned i = 0, n = diff.overloads.size(); i != n; ++i) {
|
|
auto &overload = diff.overloads[i];
|
|
auto *locator = overload.locator;
|
|
|
|
// If there is only one overload difference, it's the best.
|
|
if (n == 1) {
|
|
bestOverload = i;
|
|
break;
|
|
}
|
|
|
|
// If there are multiple overload sets involved, let's pick the
|
|
// one that has choices with different types, because that is
|
|
// most likely the source of ambiguity.
|
|
{
|
|
auto overloadTy = getOverloadChoiceType(locator, solutions.front());
|
|
if (std::all_of(solutions.begin() + 1, solutions.end(),
|
|
[&](const Solution &solution) {
|
|
return overloadTy->isEqual(
|
|
getOverloadChoiceType(locator, solution));
|
|
}))
|
|
continue;
|
|
}
|
|
|
|
ASTNode anchor;
|
|
|
|
// Simplification of member locator would produce a base expression,
|
|
// this is what we want for diagnostics but not for comparisons here
|
|
// because base expression is located at a different depth which would
|
|
// lead to incorrect results if both reference and base expression are
|
|
// ambiguous e.g. `test[x].count` if both `[x]` and `count` are ambiguous
|
|
// than simplification of `count` would produce `[x]` which is incorrect.
|
|
if (locator->isLastElement<LocatorPathElt::Member>() ||
|
|
locator->isLastElement<LocatorPathElt::ConstructorMember>()) {
|
|
anchor = locator->getAnchor();
|
|
} else {
|
|
anchor = simplifyLocatorToAnchor(overload.locator);
|
|
}
|
|
|
|
// If we can't resolve the locator to an anchor with no path,
|
|
// we can't diagnose this well.
|
|
if (!anchor)
|
|
continue;
|
|
|
|
// Index and Depth is only applicable to expressions.
|
|
unsigned index = 0;
|
|
unsigned depth = 0;
|
|
|
|
if (auto *expr = getAsExpr(anchor)) {
|
|
auto it = indexMap.find(expr);
|
|
if (it == indexMap.end())
|
|
continue;
|
|
|
|
index = it->second;
|
|
|
|
auto optDepth = getExprDepth(expr);
|
|
if (!optDepth)
|
|
continue;
|
|
|
|
depth = *optDepth;
|
|
}
|
|
|
|
// If we don't have a name to hang on to, it'll be hard to diagnose this
|
|
// overload.
|
|
if (!getOverloadChoiceName(overload.choices))
|
|
continue;
|
|
|
|
unsigned distinctOverloads = countDistinctOverloads(overload.choices);
|
|
|
|
// We need at least two overloads to make this interesting.
|
|
if (distinctOverloads < 2)
|
|
continue;
|
|
|
|
// If we have more distinct overload choices for this locator than for
|
|
// prior locators, just keep this locator.
|
|
auto thisScore = score(depth, index, distinctOverloads);
|
|
if (thisScore > bestScore) {
|
|
bestScore = thisScore;
|
|
bestOverload = i;
|
|
continue;
|
|
}
|
|
|
|
// We have better results. Ignore this one.
|
|
}
|
|
|
|
// FIXME: Should be able to pick the best locator, e.g., based on some
|
|
// depth-first numbering of expressions.
|
|
if (bestOverload) {
|
|
auto &overload = diff.overloads[*bestOverload];
|
|
// FIXME: We would prefer to emit the name as written, but that information
|
|
// is not sufficiently centralized in the AST.
|
|
DeclNameRef name(getOverloadChoiceName(overload.choices));
|
|
auto anchor = simplifyLocatorToAnchor(overload.locator);
|
|
if (!anchor) {
|
|
// It's not clear that this is actually valid. Just use the overload's
|
|
// anchor for release builds, but assert so we can properly diagnose
|
|
// this case if it happens to be hit. Note that the overload will
|
|
// *always* be anchored, otherwise everything would be broken, ie. this
|
|
// assertion would be the least of our worries.
|
|
anchor = overload.locator->getAnchor();
|
|
assert(false && "locator could not be simplified to anchor");
|
|
}
|
|
|
|
// Emit the ambiguity diagnostic.
|
|
auto &DE = getASTContext().Diags;
|
|
DE.diagnose(getLoc(anchor),
|
|
name.isOperator() ? diag::ambiguous_operator_ref
|
|
: diag::ambiguous_decl_ref,
|
|
name);
|
|
|
|
TrailingClosureAmbiguityFailure failure(solutions, anchor,
|
|
overload.choices);
|
|
if (failure.diagnoseAsNote())
|
|
return true;
|
|
|
|
// Emit candidates. Use a SmallPtrSet to make sure only emit a particular
|
|
// candidate once. FIXME: Why is one candidate getting into the overload
|
|
// set multiple times? (See also tryDiagnoseTrailingClosureAmbiguity.)
|
|
SmallPtrSet<Decl *, 8> EmittedDecls;
|
|
for (auto choice : overload.choices) {
|
|
switch (choice.getKind()) {
|
|
case OverloadChoiceKind::Decl:
|
|
case OverloadChoiceKind::DeclViaDynamic:
|
|
case OverloadChoiceKind::DeclViaBridge:
|
|
case OverloadChoiceKind::DeclViaUnwrappedOptional: {
|
|
// FIXME: show deduced types, etc, etc.
|
|
auto decl = choice.getDecl();
|
|
if (EmittedDecls.insert(decl).second) {
|
|
auto declModule = decl->getDeclContext()->getParentModule();
|
|
bool printModuleName = declModule != DC->getParentModule();
|
|
DE.diagnose(decl, diag::found_candidate_in_module,
|
|
printModuleName, declModule);
|
|
}
|
|
break;
|
|
}
|
|
case OverloadChoiceKind::KeyPathApplication:
|
|
case OverloadChoiceKind::DynamicMemberLookup:
|
|
case OverloadChoiceKind::KeyPathDynamicMemberLookup:
|
|
// Skip key path applications and dynamic member lookups, since we don't
|
|
// want them to noise up unrelated subscript diagnostics.
|
|
break;
|
|
|
|
case OverloadChoiceKind::TupleIndex:
|
|
case OverloadChoiceKind::MaterializePack:
|
|
case OverloadChoiceKind::ExtractFunctionIsolation:
|
|
// FIXME: Actually diagnose something here.
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// FIXME: If we inferred different types for literals (for example),
|
|
// could diagnose ambiguity that way as well.
|
|
|
|
return false;
|
|
}
|
|
|
|
void OpenGenericTypeRequirements::operator()(GenericTypeDecl *decl,
|
|
TypeSubstitutionFn subst) const {
|
|
auto *outerDC = decl->getDeclContext();
|
|
auto sig = decl->getGenericSignature();
|
|
|
|
// In principle we shouldn't need to open the generic parameters here, we
|
|
// could just open the requirements using the substituted arguments directly,
|
|
// but that doesn't allow us to correctly handle requirement fix coalescing
|
|
// in `isFixedRequirement`. So instead we open the generic parameters and
|
|
// then bind the resulting type variables to the substituted args.
|
|
SmallVector<OpenedType, 4> replacements;
|
|
cs.openGenericParameters(outerDC, sig, replacements, locator,
|
|
preparedOverload);
|
|
|
|
// FIXME: Get rid of fixmeAllowDuplicates. This is the same issue as in
|
|
// `openUnboundGenericType`; both `applyUnboundGenericArguments` &
|
|
// `replaceInferableTypesWithTypeVars` can open multiple different generic
|
|
// types with the same locator. For the former we ought to plumb through the
|
|
// TypeRepr and use that to distinguish the locator. For the latter we ought
|
|
// to try migrating clients off it, pushing the opening up to type resolution.
|
|
cs.recordOpenedTypes(locator, replacements, preparedOverload,
|
|
/*fixmeAllowDuplicates*/ true);
|
|
|
|
for (auto [gp, typeVar] : replacements)
|
|
cs.addConstraint(ConstraintKind::Bind, typeVar, subst(gp), locator);
|
|
|
|
auto openType = [&](Type ty) -> Type {
|
|
return cs.openType(ty, replacements, locator, preparedOverload);
|
|
};
|
|
cs.openGenericRequirements(outerDC, sig, /*skipProtocolSelf*/ false, locator,
|
|
openType, preparedOverload);
|
|
}
|
|
|
|
ConstraintLocator *
|
|
constraints::simplifyLocator(ConstraintSystem &cs, ConstraintLocator *locator,
|
|
SourceRange &range) {
|
|
auto path = locator->getPath();
|
|
auto anchor = locator->getAnchor();
|
|
simplifyLocator(anchor, path, range);
|
|
|
|
// If we didn't simplify anything, just return the input.
|
|
if (anchor == locator->getAnchor() &&
|
|
path.size() == locator->getPath().size()) {
|
|
return locator;
|
|
}
|
|
|
|
// If the old locator didn't have any summary flags, neither will the
|
|
// simplified version, as it must contain a subset of the path elements.
|
|
if (locator->getSummaryFlags() == 0)
|
|
return cs.getConstraintLocator(anchor, path, /*summaryFlags*/ 0);
|
|
|
|
return cs.getConstraintLocator(anchor, path);
|
|
}
|
|
|
|
void constraints::simplifyLocator(ASTNode &anchor,
|
|
ArrayRef<LocatorPathElt> &path,
|
|
SourceRange &range) {
|
|
range = SourceRange();
|
|
|
|
while (!path.empty()) {
|
|
switch (path[0].getKind()) {
|
|
case ConstraintLocator::ApplyArgument: {
|
|
auto *anchorExpr = castToExpr(anchor);
|
|
// If the next element is an ApplyArgToParam, we can simplify by looking
|
|
// into the index expression.
|
|
if (path.size() < 2)
|
|
break;
|
|
|
|
auto elt = path[1].getAs<LocatorPathElt::ApplyArgToParam>();
|
|
if (!elt)
|
|
break;
|
|
|
|
// If the 3rd element is an PackElement, add the index of pack element
|
|
// within packs to locate the correct element.
|
|
std::optional<unsigned> eltPackIdx;
|
|
if (path.size() > 2) {
|
|
if (auto eltPack = path[2].getAs<LocatorPathElt::PackElement>()) {
|
|
eltPackIdx = eltPack->getIndex();
|
|
}
|
|
}
|
|
|
|
// Extract application argument.
|
|
if (auto *args = anchorExpr->getArgs()) {
|
|
if (eltPackIdx.has_value()) {
|
|
if (elt->getArgIdx() + eltPackIdx.value() < args->size()) {
|
|
anchor = args->getExpr(elt->getArgIdx() + eltPackIdx.value());
|
|
path = path.slice(3);
|
|
continue;
|
|
}
|
|
} else if (elt->getArgIdx() < args->size()) {
|
|
anchor = args->getExpr(elt->getArgIdx());
|
|
path = path.slice(2);
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ConstraintLocator::ApplyArgToParam:
|
|
llvm_unreachable("Cannot appear without ApplyArgument");
|
|
|
|
case ConstraintLocator::DynamicCallable: {
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
case ConstraintLocator::ApplyFunction: {
|
|
// Extract application function.
|
|
if (auto applyExpr = getAsExpr<ApplyExpr>(anchor)) {
|
|
anchor = applyExpr->getFn();
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
// The subscript itself is the function.
|
|
if (auto subscriptExpr = getAsExpr<SubscriptExpr>(anchor)) {
|
|
anchor = subscriptExpr;
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
// If the anchor is an unapplied decl ref, there's nothing to extract.
|
|
if (isExpr<DeclRefExpr>(anchor) || isExpr<OverloadedDeclRefExpr>(anchor)) {
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ConstraintLocator::AutoclosureResult:
|
|
case ConstraintLocator::LValueConversion:
|
|
case ConstraintLocator::DynamicType:
|
|
case ConstraintLocator::UnresolvedMember:
|
|
case ConstraintLocator::ImplicitCallAsFunction:
|
|
// Arguments in autoclosure positions, lvalue and rvalue adjustments,
|
|
// unresolved members, and implicit callAsFunction references are
|
|
// implicit.
|
|
path = path.slice(1);
|
|
continue;
|
|
|
|
case ConstraintLocator::TupleType:
|
|
case ConstraintLocator::GenericType:
|
|
path = path.slice(1);
|
|
continue;
|
|
|
|
case ConstraintLocator::NamedTupleElement:
|
|
case ConstraintLocator::TupleElement: {
|
|
// Extract tuple element.
|
|
auto elt = path[0].castTo<LocatorPathElt::AnyTupleElement>();
|
|
unsigned index = elt.getIndex();
|
|
|
|
if (auto *AE = getAsExpr<AssignExpr>(anchor)) {
|
|
if (isa<TupleExpr>(AE->getSrc())) {
|
|
anchor = AE->getSrc();
|
|
}
|
|
}
|
|
|
|
if (auto tupleExpr = getAsExpr<TupleExpr>(anchor)) {
|
|
if (index < tupleExpr->getNumElements()) {
|
|
anchor = tupleExpr->getElement(index);
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (auto *CE = getAsExpr<CollectionExpr>(anchor)) {
|
|
if (index < CE->getNumElements()) {
|
|
anchor = CE->getElement(index);
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case ConstraintLocator::ConstructorMember:
|
|
// Look through specialization first, because it doesn't play a
|
|
// functional role here.
|
|
if (auto *USE = getAsExpr<UnresolvedSpecializeExpr>(anchor)) {
|
|
anchor = USE->getSubExpr();
|
|
range = anchor.getSourceRange();
|
|
}
|
|
|
|
// - This is really an implicit 'init' MemberRef, so point at the base,
|
|
// i.e. the TypeExpr.
|
|
// - For re-declarations we'd get an overloaded reference
|
|
// with multiple choices for the same type.
|
|
if (isExpr<TypeExpr>(anchor) || isExpr<OverloadedDeclRefExpr>(anchor)) {
|
|
range = SourceRange();
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
LLVM_FALLTHROUGH;
|
|
|
|
case ConstraintLocator::Member:
|
|
if (isExpr<UnresolvedDotExpr>(anchor)) {
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
if (isa<Pattern *>(anchor)) {
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
break;
|
|
case ConstraintLocator::MemberRefBase:
|
|
if (auto UDE = getAsExpr<UnresolvedDotExpr>(anchor)) {
|
|
range = UDE->getNameLoc().getSourceRange();
|
|
anchor = UDE->getBase();
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
if (isa<Pattern *>(anchor)) {
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
|
|
case ConstraintLocator::SubscriptMember:
|
|
if (isExpr<SubscriptExpr>(anchor)) {
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case ConstraintLocator::ClosureBody:
|
|
case ConstraintLocator::ClosureResult:
|
|
if (auto CE = getAsExpr<ClosureExpr>(anchor)) {
|
|
if (CE->hasSingleExpressionBody()) {
|
|
anchor = CE->getSingleExpressionBody();
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ConstraintLocator::ClosureThrownError:
|
|
if (auto CE = getAsExpr<ClosureExpr>(anchor)) {
|
|
if (auto thrownTypeRepr = CE->getExplicitThrownTypeRepr()) {
|
|
anchor = thrownTypeRepr;
|
|
path = path.slice(1);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ConstraintLocator::CoercionOperand: {
|
|
auto *CE = castToExpr<CoerceExpr>(anchor);
|
|
anchor = CE->getSubExpr()->getValueProvidingExpr();
|
|
path = path.slice(1);
|
|
// When in a argument function type on a coercion context
|
|
// look past the argument, because is just for identify the
|
|
// argument type that is being matched.
|
|
if (!path.empty() && path[0].is<LocatorPathElt::FunctionArgument>()) {
|
|
path = path.slice(1);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
case ConstraintLocator::GlobalActorType:
|
|
case ConstraintLocator::ContextualType: {
|
|
// This was just for identifying purposes, strip it off.
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
case ConstraintLocator::KeyPathComponent: {
|
|
auto elt = path[0].castTo<LocatorPathElt::KeyPathComponent>();
|
|
|
|
// If the next element is an ApplyArgument, we can simplify by looking
|
|
// into the index expression.
|
|
if (path.size() < 3 ||
|
|
path[1].getKind() != ConstraintLocator::ApplyArgument)
|
|
break;
|
|
|
|
auto applyArgElt = path[2].getAs<LocatorPathElt::ApplyArgToParam>();
|
|
if (!applyArgElt)
|
|
break;
|
|
|
|
auto argIdx = applyArgElt->getArgIdx();
|
|
if (auto *kpe = getAsExpr<KeyPathExpr>(anchor)) {
|
|
auto component = kpe->getComponents()[elt.getIndex()];
|
|
auto *args = component.getArgs();
|
|
assert(args && "Trying to apply a component without args?");
|
|
if (argIdx < args->size()) {
|
|
anchor = args->getExpr(argIdx);
|
|
path = path.slice(3);
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ConstraintLocator::Condition: {
|
|
if (auto *condStmt = getAsStmt<LabeledConditionalStmt>(anchor)) {
|
|
anchor = &condStmt->getCond().front();
|
|
} else {
|
|
anchor = castToExpr<TernaryExpr>(anchor)->getCondExpr();
|
|
}
|
|
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
case ConstraintLocator::TernaryBranch: {
|
|
auto branch = path[0].castTo<LocatorPathElt::TernaryBranch>();
|
|
|
|
if (auto *ifStmt = getAsStmt<IfStmt>(anchor)) {
|
|
anchor =
|
|
branch.forThen() ? ifStmt->getThenStmt() : ifStmt->getElseStmt();
|
|
} else {
|
|
auto *ifExpr = castToExpr<TernaryExpr>(anchor);
|
|
anchor =
|
|
branch.forThen() ? ifExpr->getThenExpr() : ifExpr->getElseExpr();
|
|
}
|
|
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
case ConstraintLocator::SingleValueStmtResult: {
|
|
auto branchElt = path[0].castTo<LocatorPathElt::SingleValueStmtResult>();
|
|
auto exprIdx = branchElt.getIndex();
|
|
auto *SVE = castToExpr<SingleValueStmtExpr>(anchor);
|
|
SmallVector<Expr *, 4> scratch;
|
|
anchor = SVE->getResultExprs(scratch)[exprIdx];
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
case ConstraintLocator::KeyPathDynamicMember:
|
|
case ConstraintLocator::ImplicitDynamicMemberSubscript: {
|
|
// Key path dynamic member lookup should be completely transparent.
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
case ConstraintLocator::ArgumentAttribute: {
|
|
// At this point we should have already found argument expression
|
|
// this attribute belongs to, so we can leave this element in place
|
|
// because it points out exact location useful for diagnostics.
|
|
break;
|
|
}
|
|
|
|
case ConstraintLocator::ResultBuilderBodyResult: {
|
|
path = path.slice(1);
|
|
break;
|
|
}
|
|
|
|
case ConstraintLocator::UnresolvedMemberChainResult: {
|
|
auto *resultExpr = castToExpr<UnresolvedMemberChainResultExpr>(anchor);
|
|
anchor = resultExpr->getSubExpr();
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
case ConstraintLocator::SyntacticElement: {
|
|
auto bodyElt = path[0].castTo<LocatorPathElt::SyntacticElement>();
|
|
anchor = bodyElt.getElement();
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
case ConstraintLocator::PatternMatch: {
|
|
auto patternElt = path[0].castTo<LocatorPathElt::PatternMatch>();
|
|
anchor = patternElt.getPattern();
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
case ConstraintLocator::EnumPatternImplicitCastMatch: {
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
case ConstraintLocator::PackType:
|
|
case ConstraintLocator::ParentType:
|
|
case ConstraintLocator::KeyPathType:
|
|
case ConstraintLocator::InstanceType:
|
|
case ConstraintLocator::PlaceholderType:
|
|
case ConstraintLocator::SequenceElementType:
|
|
case ConstraintLocator::ConstructorMemberType:
|
|
case ConstraintLocator::ExistentialConstraintType:
|
|
case ConstraintLocator::ProtocolCompositionMemberType:
|
|
break;
|
|
|
|
case ConstraintLocator::GenericArgument:
|
|
case ConstraintLocator::FunctionArgument:
|
|
case ConstraintLocator::SynthesizedArgument:
|
|
break;
|
|
|
|
case ConstraintLocator::FunctionResult:
|
|
case ConstraintLocator::DynamicLookupResult:
|
|
case ConstraintLocator::KeyPathComponentResult:
|
|
break;
|
|
|
|
case ConstraintLocator::GenericParameter:
|
|
break;
|
|
|
|
case ConstraintLocator::ThrownErrorType:
|
|
break;
|
|
|
|
case ConstraintLocator::OpenedGeneric:
|
|
case ConstraintLocator::OpenedOpaqueArchetype:
|
|
break;
|
|
|
|
case ConstraintLocator::KeyPathRoot:
|
|
case ConstraintLocator::KeyPathValue:
|
|
break;
|
|
|
|
case ConstraintLocator::ProtocolRequirement:
|
|
case ConstraintLocator::ConditionalRequirement:
|
|
case ConstraintLocator::ConformanceRequirement:
|
|
case ConstraintLocator::TypeParameterRequirement:
|
|
break;
|
|
|
|
case ConstraintLocator::PackElement:
|
|
case ConstraintLocator::PackShape:
|
|
case ConstraintLocator::PackExpansionType:
|
|
break;
|
|
|
|
case ConstraintLocator::PackExpansionPattern: {
|
|
if (auto *expansion = getAsExpr<PackExpansionExpr>(anchor))
|
|
anchor = expansion->getPatternExpr();
|
|
|
|
path = path.slice(1);
|
|
break;
|
|
}
|
|
|
|
case ConstraintLocator::PatternBindingElement: {
|
|
auto pattern = path[0].castTo<LocatorPathElt::PatternBindingElement>();
|
|
auto *patternBinding = cast<PatternBindingDecl>(cast<Decl *>(anchor));
|
|
anchor = patternBinding->getInit(pattern.getIndex());
|
|
// If this pattern is uninitialized, let's use it as anchor.
|
|
if (!anchor)
|
|
anchor = patternBinding->getPattern(pattern.getIndex());
|
|
path = path.slice(1);
|
|
continue;
|
|
}
|
|
|
|
case ConstraintLocator::NamedPatternDecl: {
|
|
auto pattern = cast<NamedPattern>(cast<Pattern *>(anchor));
|
|
anchor = pattern->getDecl();
|
|
path = path.slice(1);
|
|
break;
|
|
}
|
|
|
|
case ConstraintLocator::AnyPatternDecl: {
|
|
// This element is just a marker for `_` pattern since it doesn't
|
|
// have a declaration. We need to make sure that it only appaears
|
|
// when anchored on `AnyPattern`.
|
|
assert(getAsPattern<AnyPattern>(anchor));
|
|
path = path.slice(1);
|
|
break;
|
|
}
|
|
|
|
case ConstraintLocator::ImplicitConversion:
|
|
break;
|
|
|
|
case ConstraintLocator::Witness:
|
|
case ConstraintLocator::WrappedValue:
|
|
case ConstraintLocator::OptionalInjection:
|
|
case ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice:
|
|
case ConstraintLocator::FallbackType:
|
|
case ConstraintLocator::KeyPathSubscriptIndex:
|
|
case ConstraintLocator::ExistentialMemberAccessConversion:
|
|
break;
|
|
}
|
|
|
|
// If we get here, we couldn't simplify the path further.
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASTNode constraints::simplifyLocatorToAnchor(ConstraintLocator *locator) {
|
|
if (!locator)
|
|
return nullptr;
|
|
|
|
auto anchor = locator->getAnchor();
|
|
if (!anchor)
|
|
return {};
|
|
|
|
SourceRange range;
|
|
auto path = locator->getPath();
|
|
simplifyLocator(anchor, path, range);
|
|
|
|
// We only want the new anchor if all the path elements have been simplified
|
|
// away.
|
|
return path.empty() ? anchor : nullptr;
|
|
}
|
|
|
|
Expr *constraints::getArgumentExpr(ASTNode node, unsigned index) {
|
|
auto *expr = getAsExpr(node);
|
|
if (!expr)
|
|
return nullptr;
|
|
|
|
auto *argList = expr->getArgs();
|
|
if (!argList)
|
|
return nullptr;
|
|
|
|
if (index >= argList->size())
|
|
return nullptr;
|
|
|
|
return argList->getExpr(index);
|
|
}
|
|
|
|
bool constraints::isAutoClosureArgument(Expr *argExpr) {
|
|
if (!argExpr)
|
|
return false;
|
|
|
|
if (auto *DRE = dyn_cast<DeclRefExpr>(argExpr)) {
|
|
if (auto *param = dyn_cast<ParamDecl>(DRE->getDecl()))
|
|
return param->isAutoClosure();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool constraints::hasAppliedSelf(ConstraintSystem &cs,
|
|
const OverloadChoice &choice) {
|
|
return hasAppliedSelf(choice, [&cs](Type type) -> Type {
|
|
return cs.getFixedTypeRecursive(type, /*wantRValue=*/true);
|
|
});
|
|
}
|
|
|
|
bool constraints::hasAppliedSelf(const Solution &S,
|
|
const OverloadChoice &choice) {
|
|
return hasAppliedSelf(choice, [&](Type type) -> Type {
|
|
return S.simplifyType(type);
|
|
});
|
|
}
|
|
|
|
bool constraints::hasAppliedSelf(const OverloadChoice &choice,
|
|
llvm::function_ref<Type(Type)> getFixedType) {
|
|
auto *decl = choice.getDeclOrNull();
|
|
if (!decl)
|
|
return false;
|
|
|
|
auto baseType = choice.getBaseType();
|
|
if (baseType)
|
|
baseType = getFixedType(baseType)->getRValueType();
|
|
|
|
// In most cases where we reference a declaration with a curried self
|
|
// parameter, it gets dropped from the type of the reference.
|
|
return decl->hasCurriedSelf() &&
|
|
doesMemberRefApplyCurriedSelf(baseType, decl);
|
|
}
|
|
|
|
/// Check whether given type conforms to `RawRepresentable` protocol
|
|
/// and return the witness type.
|
|
Type constraints::isRawRepresentable(ConstraintSystem &cs, Type type) {
|
|
auto rawReprType = TypeChecker::getProtocol(
|
|
cs.getASTContext(), SourceLoc(), KnownProtocolKind::RawRepresentable);
|
|
if (!rawReprType)
|
|
return Type();
|
|
|
|
auto conformance = cs.lookupConformance(type, rawReprType);
|
|
if (conformance.isInvalid())
|
|
return Type();
|
|
|
|
return conformance.getTypeWitnessByName(cs.getASTContext().Id_RawValue);
|
|
}
|
|
|
|
void ConstraintSystem::generateOverloadConstraints(
|
|
SmallVectorImpl<Constraint *> &constraints, Type type,
|
|
ArrayRef<OverloadChoice> choices, DeclContext *useDC,
|
|
ConstraintLocator *locator, std::optional<unsigned> favoredIndex,
|
|
bool requiresFix,
|
|
llvm::function_ref<ConstraintFix *(unsigned, const OverloadChoice &)>
|
|
getFix) {
|
|
auto recordChoice = [&](SmallVectorImpl<Constraint *> &choices,
|
|
unsigned index, const OverloadChoice &overload,
|
|
bool isFavored = false) {
|
|
auto *fix = getFix(index, overload);
|
|
// If fix is required but it couldn't be determined, this
|
|
// choice has be filtered out.
|
|
if (requiresFix && !fix)
|
|
return;
|
|
|
|
auto *choice = Constraint::createBindOverload(*this, type, overload,
|
|
useDC, fix, locator);
|
|
|
|
if (isFavored)
|
|
choice->setFavored();
|
|
|
|
choices.push_back(choice);
|
|
};
|
|
|
|
if (favoredIndex) {
|
|
const auto &choice = choices[*favoredIndex];
|
|
assert(
|
|
(!choice.isDecl() || !isDeclUnavailable(choice.getDecl(), locator)) &&
|
|
"Cannot make unavailable decl favored!");
|
|
recordChoice(constraints, *favoredIndex, choice, /*isFavored=*/true);
|
|
}
|
|
|
|
for (auto index : indices(choices)) {
|
|
if (favoredIndex && (*favoredIndex == index))
|
|
continue;
|
|
|
|
recordChoice(constraints, index, choices[index]);
|
|
}
|
|
}
|
|
|
|
ConstraintLocator *
|
|
ConstraintSystem::getArgumentInfoLocator(ConstraintLocator *locator) {
|
|
auto anchor = locator->getAnchor();
|
|
|
|
// An empty locator which code completion uses for member references.
|
|
if (anchor.isNull() && locator->getPath().empty())
|
|
return nullptr;
|
|
|
|
if (locator->findLast<LocatorPathElt::ImplicitConversion>())
|
|
return locator;
|
|
|
|
// Applies and unresolved member exprs can have callee locators that are
|
|
// dependent on the type of their function, which may not have been resolved
|
|
// yet. Therefore we need to handle them specially.
|
|
if (auto *apply = getAsExpr<ApplyExpr>(anchor)) {
|
|
auto *fnExpr = getArgumentLabelTargetExpr(apply->getFn());
|
|
return getConstraintLocator(fnExpr);
|
|
}
|
|
|
|
if (auto *UME = getAsExpr<UnresolvedMemberExpr>(anchor))
|
|
return getConstraintLocator(UME);
|
|
|
|
// All implicit x[dynamicMember:] subscript calls can share the same argument
|
|
// list.
|
|
if (locator->findLast<LocatorPathElt::ImplicitDynamicMemberSubscript>()) {
|
|
return getConstraintLocator(
|
|
ASTNode(), LocatorPathElt::ImplicitDynamicMemberSubscript());
|
|
}
|
|
|
|
auto path = locator->getPath();
|
|
{
|
|
// If this is for a dynamic member reference, the argument info is for the
|
|
// original call-site, which we can get by stripping away the
|
|
// KeyPathDynamicMember elements.
|
|
auto iter = path.begin();
|
|
if (locator->findFirst<LocatorPathElt::KeyPathDynamicMember>(iter)) {
|
|
ArrayRef<LocatorPathElt> newPath(path.begin(), iter);
|
|
return getConstraintLocator(anchor, newPath);
|
|
}
|
|
}
|
|
|
|
return getCalleeLocator(locator);
|
|
}
|
|
|
|
ArgumentList *ConstraintSystem::getArgumentList(ConstraintLocator *locator) {
|
|
if (!locator)
|
|
return nullptr;
|
|
|
|
if (auto *infoLocator = getArgumentInfoLocator(locator)) {
|
|
auto known = ArgumentLists.find(infoLocator);
|
|
if (known != ArgumentLists.end())
|
|
return known->second;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void ConstraintSystem::recordArgumentList(ConstraintLocator *locator,
|
|
ArgumentList *args) {
|
|
bool inserted = ArgumentLists.insert({locator, args}).second;
|
|
ASSERT(inserted);
|
|
|
|
if (solverState)
|
|
recordChange(SolverTrail::Change::RecordedArgumentList(locator));
|
|
}
|
|
|
|
void ConstraintSystem::associateArgumentList(ConstraintLocator *locator,
|
|
ArgumentList *args) {
|
|
ASSERT(locator && locator->getAnchor());
|
|
auto *argLoc = getArgumentInfoLocator(locator);
|
|
recordArgumentList(argLoc, args);
|
|
}
|
|
|
|
ArgumentList *Solution::getArgumentList(ConstraintLocator *locator) const {
|
|
if (!locator)
|
|
return nullptr;
|
|
|
|
if (auto *infoLocator = constraintSystem->getArgumentInfoLocator(locator)) {
|
|
auto known = argumentLists.find(infoLocator);
|
|
if (known != argumentLists.end())
|
|
return known->second;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
std::optional<ConversionRestrictionKind>
|
|
Solution::getConversionRestriction(CanType type1, CanType type2) const {
|
|
auto restriction = ConstraintRestrictions.find({type1, type2});
|
|
if (restriction != ConstraintRestrictions.end())
|
|
return restriction->second;
|
|
return std::nullopt;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
/// Given an apply expr, returns true if it is expected to have a direct callee
|
|
/// overload, resolvable using `getChoiceFor`. Otherwise, returns false.
|
|
static bool shouldHaveDirectCalleeOverload(const CallExpr *callExpr) {
|
|
auto *fnExpr = callExpr->getDirectCallee();
|
|
|
|
// An apply of an apply/subscript doesn't have a direct callee.
|
|
if (isa<ApplyExpr>(fnExpr) || isa<SubscriptExpr>(fnExpr))
|
|
return false;
|
|
|
|
// Applies of closures don't have callee overloads.
|
|
if (isa<ClosureExpr>(fnExpr))
|
|
return false;
|
|
|
|
// No direct callee for a try!/try?.
|
|
if (isa<ForceTryExpr>(fnExpr) || isa<OptionalTryExpr>(fnExpr))
|
|
return false;
|
|
|
|
// If we have an intermediate cast, there's no direct callee.
|
|
if (isa<ExplicitCastExpr>(fnExpr))
|
|
return false;
|
|
|
|
// No direct callee for a ternary expr.
|
|
if (isa<TernaryExpr>(fnExpr))
|
|
return false;
|
|
|
|
// Assume that anything else would have a direct callee.
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
ASTNode ConstraintSystem::includingParentApply(ASTNode node) {
|
|
if (auto *expr = getAsExpr(node)) {
|
|
if (auto apply = getAsExpr<ApplyExpr>(getParentExpr(expr))) {
|
|
if (apply->getFn() == expr)
|
|
return apply;
|
|
}
|
|
}
|
|
return node;
|
|
}
|
|
|
|
GenericEnvironment *
|
|
Solution::getPackExpansionEnvironment(PackExpansionExpr *expr) const {
|
|
auto iter = PackExpansionEnvironments.find(expr);
|
|
if (iter == PackExpansionEnvironments.end())
|
|
return nullptr;
|
|
|
|
return iter->second;
|
|
}
|
|
|
|
std::optional<FunctionArgApplyInfo>
|
|
Solution::getFunctionArgApplyInfo(ConstraintLocator *locator) const {
|
|
// It's only valid to use `&` in argument positions, but we need
|
|
// to figure out exactly where it was used.
|
|
if (auto *argExpr = getAsExpr<InOutExpr>(locator->getAnchor())) {
|
|
auto *argLoc = getConstraintSystem().getArgumentLocator(argExpr);
|
|
if (!argLoc)
|
|
return std::nullopt;
|
|
|
|
locator = argLoc;
|
|
}
|
|
|
|
auto anchor = locator->getAnchor();
|
|
auto path = locator->getPath();
|
|
|
|
// Look for the apply-arg-to-param element in the locator's path. We may
|
|
// have to look through other elements that are generated from an argument
|
|
// conversion such as GenericArgument for an optional-to-optional conversion,
|
|
// and OptionalInjection for a value-to-optional conversion.
|
|
auto iter = path.rbegin();
|
|
auto applyArgElt = locator->findLast<LocatorPathElt::ApplyArgToParam>(iter);
|
|
if (!applyArgElt)
|
|
return std::nullopt;
|
|
|
|
#ifndef NDEBUG
|
|
auto nextIter = iter + 1;
|
|
assert(!locator->findLast<LocatorPathElt::ApplyArgToParam>(nextIter) &&
|
|
"Multiple ApplyArgToParam components?");
|
|
#endif
|
|
|
|
// Form a new locator that ends at the apply-arg-to-param element, and
|
|
// simplify it to get the full argument expression.
|
|
auto argPath = path.drop_back(iter - path.rbegin());
|
|
auto *argLocator = getConstraintLocator(anchor, argPath);
|
|
|
|
auto *argExpr = castToExpr(simplifyLocatorToAnchor(argLocator));
|
|
|
|
// If we were unable to simplify down to the argument expression, we don't
|
|
// know what this is.
|
|
if (!argExpr)
|
|
return std::nullopt;
|
|
|
|
auto *argList = getArgumentList(argLocator);
|
|
if (!argList)
|
|
return std::nullopt;
|
|
|
|
std::optional<OverloadChoice> choice;
|
|
Type rawFnType;
|
|
auto *calleeLocator = getCalleeLocator(argLocator);
|
|
if (auto overload = getOverloadChoiceIfAvailable(calleeLocator)) {
|
|
// If we have resolved an overload for the callee, then use that to get the
|
|
// function type and callee.
|
|
choice = overload->choice;
|
|
rawFnType = overload->adjustedOpenedType;
|
|
} else {
|
|
// If we didn't resolve an overload for the callee, we should be dealing
|
|
// with a call of an arbitrary function expr.
|
|
auto *call = castToExpr<CallExpr>(anchor);
|
|
rawFnType = getType(call->getFn());
|
|
|
|
// If callee couldn't be resolved due to expression
|
|
// issues e.g. it's a reference to an invalid member
|
|
// let's just return here.
|
|
if (simplifyType(rawFnType)->is<UnresolvedType>())
|
|
return std::nullopt;
|
|
|
|
// A tuple construction is spelled in the AST as a function call, but
|
|
// is really more like a tuple conversion.
|
|
if (auto metaTy = simplifyType(rawFnType)->getAs<MetatypeType>()) {
|
|
if (metaTy->getInstanceType()->is<TupleType>())
|
|
return std::nullopt;
|
|
}
|
|
|
|
assert(!shouldHaveDirectCalleeOverload(call) &&
|
|
"Should we have resolved a callee for this?");
|
|
}
|
|
|
|
// Try to resolve the function type by loading lvalues and looking through
|
|
// optional types, which can occur for expressions like `fn?(5)`.
|
|
auto *fnType = simplifyType(rawFnType)
|
|
->getRValueType()
|
|
->lookThroughAllOptionalTypes()
|
|
->getAs<FunctionType>();
|
|
if (!fnType)
|
|
return std::nullopt;
|
|
|
|
// Resolve the interface type for the function. Note that this may not be a
|
|
// function type, for example it could be a generic parameter.
|
|
Type fnInterfaceType;
|
|
auto *callee = choice ? choice->getDeclOrNull() : nullptr;
|
|
if (callee && callee->hasInterfaceType()) {
|
|
// If we have a callee with an interface type, we can use it. This is
|
|
// preferable to simplifyType for the function, as this will allow us to get
|
|
// a GenericFunctionType for generic decls.
|
|
//
|
|
// Note that it's possible to find a callee without an interface type. This
|
|
// can happen for example with closure parameters, where the interface type
|
|
// isn't set until the solution is applied. In that case, use simplifyType.
|
|
fnInterfaceType = callee->getInterfaceType();
|
|
|
|
// Strip off the curried self parameter if necessary.
|
|
if (hasAppliedSelf(*this, *choice))
|
|
fnInterfaceType = fnInterfaceType->castTo<AnyFunctionType>()->getResult();
|
|
|
|
#ifndef NDEBUG
|
|
// If variadic generics are not involved, interface type should
|
|
// always match applied type.
|
|
if (auto *fn = fnInterfaceType->getAs<AnyFunctionType>()) {
|
|
if (llvm::none_of(fn->getParams(), [&](const auto ¶m) {
|
|
return param.getPlainType()->hasParameterPack();
|
|
})) {
|
|
assert(fn->getNumParams() == fnType->getNumParams() &&
|
|
"Parameter mismatch?");
|
|
}
|
|
}
|
|
#endif
|
|
} else {
|
|
fnInterfaceType = simplifyType(rawFnType, /*wantInterfaceType*/ true);
|
|
}
|
|
|
|
auto argIdx = applyArgElt->getArgIdx();
|
|
auto paramIdx = applyArgElt->getParamIdx();
|
|
|
|
return FunctionArgApplyInfo::get(argList, argExpr, argIdx,
|
|
simplifyType(getType(argExpr)), paramIdx,
|
|
fnInterfaceType, fnType, callee);
|
|
}
|
|
|
|
bool constraints::isKnownKeyPathType(Type type) {
|
|
return type->isKeyPath() || type->isWritableKeyPath() ||
|
|
type->isReferenceWritableKeyPath() || type->isPartialKeyPath() ||
|
|
type->isAnyKeyPath();
|
|
}
|
|
|
|
bool constraints::isTypeErasedKeyPathType(Type type) {
|
|
assert(type);
|
|
|
|
if (type->isPartialKeyPath() || type->isAnyKeyPath())
|
|
return true;
|
|
|
|
if (!type->isExistentialType())
|
|
return false;
|
|
|
|
auto superclass = type->getSuperclass();
|
|
return superclass ? isTypeErasedKeyPathType(superclass) : false;
|
|
}
|
|
|
|
bool constraints::hasResultExpr(ClosureExpr *closure) {
|
|
auto &ctx = closure->getASTContext();
|
|
return evaluateOrDefault(ctx.evaluator, ClosureHasResultExprRequest{closure},
|
|
false);
|
|
}
|
|
|
|
Type constraints::getConcreteReplacementForProtocolSelfType(ValueDecl *member) {
|
|
auto *DC = member->getDeclContext();
|
|
|
|
if (!DC->getSelfProtocolDecl())
|
|
return Type();
|
|
|
|
auto sig = member->getInnermostDeclContext()->getGenericSignatureOfContext();
|
|
auto selfTy = DC->getSelfInterfaceType();
|
|
return sig->getConcreteType(selfTy);
|
|
}
|
|
|
|
static bool isOperator(Expr *expr, StringRef expectedName) {
|
|
auto name = getOperatorName(expr);
|
|
return name ? name->is(expectedName) : false;
|
|
}
|
|
|
|
std::optional<Identifier> constraints::getOperatorName(Expr *expr) {
|
|
ValueDecl *choice = nullptr;
|
|
if (auto *ODRE = dyn_cast_or_null<OverloadedDeclRefExpr>(expr)) {
|
|
choice = ODRE->getDecls().front();
|
|
} else if (auto *DRE = dyn_cast_or_null<DeclRefExpr>(expr)) {
|
|
choice = DRE->getDecl();
|
|
} else {
|
|
return std::nullopt;
|
|
}
|
|
|
|
if (auto *FD = dyn_cast_or_null<AbstractFunctionDecl>(choice))
|
|
return FD->getBaseIdentifier();
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
bool constraints::isPatternMatchingOperator(ASTNode node) {
|
|
auto *expr = getAsExpr(node);
|
|
if (!expr) return false;
|
|
|
|
return isOperator(expr, "~=");
|
|
}
|
|
|
|
bool constraints::isStandardComparisonOperator(ASTNode node) {
|
|
auto *expr = getAsExpr(node);
|
|
if (!expr) return false;
|
|
|
|
if (auto opName = getOperatorName(expr)) {
|
|
return opName->isStandardComparisonOperator();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ConstraintLocator *ConstraintSystem::getArgumentLocator(Expr *expr) {
|
|
auto *application = getParentExpr(expr);
|
|
if (!application)
|
|
return nullptr;
|
|
|
|
// Drop all of the semantically insignificant exprs that might be wrapping an
|
|
// argument e.g. `test(((42)))`
|
|
while (application->getSemanticsProvidingExpr() == expr) {
|
|
application = getParentExpr(application);
|
|
if (!application)
|
|
return nullptr;
|
|
}
|
|
|
|
ArgumentList *argList = application->getArgs();
|
|
if (!argList && !isa<KeyPathExpr>(application))
|
|
return nullptr;
|
|
|
|
ConstraintLocator *loc = nullptr;
|
|
if (auto *KP = dyn_cast<KeyPathExpr>(application)) {
|
|
auto idx = KP->findComponentWithSubscriptArg(expr);
|
|
if (!idx)
|
|
return nullptr;
|
|
loc = getConstraintLocator(KP, {LocatorPathElt::KeyPathComponent(*idx)});
|
|
argList = KP->getComponents()[*idx].getArgs();
|
|
} else {
|
|
loc = getConstraintLocator(application);
|
|
}
|
|
assert(argList);
|
|
|
|
auto argIdx = argList->findArgumentExpr(expr);
|
|
if (!argIdx)
|
|
return nullptr;
|
|
|
|
ParameterTypeFlags flags;
|
|
flags = flags.withInOut(argList->get(*argIdx).isInOut());
|
|
return getConstraintLocator(
|
|
loc, {LocatorPathElt::ApplyArgument(),
|
|
LocatorPathElt::ApplyArgToParam(*argIdx, *argIdx, flags)});
|
|
}
|
|
|
|
bool constraints::isOperatorArgument(ConstraintLocator *locator,
|
|
StringRef expectedOperator) {
|
|
if (!locator->findLast<LocatorPathElt::ApplyArgToParam>())
|
|
return false;
|
|
|
|
if (auto *AE = getAsExpr<ApplyExpr>(locator->getAnchor())) {
|
|
if (isa<PrefixUnaryExpr>(AE) || isa<BinaryExpr>(AE) ||
|
|
isa<PostfixUnaryExpr>(AE))
|
|
return expectedOperator.empty() ||
|
|
isOperator(AE->getFn(), expectedOperator);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool constraints::isArgumentOfPatternMatchingOperator(
|
|
ConstraintLocator *locator) {
|
|
auto *binaryOp = getAsExpr<BinaryExpr>(locator->getAnchor());
|
|
if (!(binaryOp && binaryOp->isImplicit()))
|
|
return false;
|
|
return isPatternMatchingOperator(binaryOp->getFn());
|
|
}
|
|
|
|
bool constraints::isArgumentOfReferenceEqualityOperator(
|
|
ConstraintLocator *locator) {
|
|
return isOperatorArgument(locator, "===") ||
|
|
isOperatorArgument(locator, "!==");
|
|
}
|
|
|
|
bool ConstraintSystem::isArgumentOfImportedDecl(
|
|
ConstraintLocatorBuilder locator) {
|
|
SmallVector<LocatorPathElt, 4> path;
|
|
auto anchor = locator.getLocatorParts(path);
|
|
|
|
if (path.empty())
|
|
return false;
|
|
|
|
while (!path.empty()) {
|
|
const auto &last = path.back();
|
|
|
|
// Drop all of the `optional payload` or `generic argument`
|
|
// locator elements at the end of the path, they came from
|
|
// either value-to-optional promotion or optional-to-optional
|
|
// conversion.
|
|
if (last.is<LocatorPathElt::OptionalInjection>() ||
|
|
last.is<LocatorPathElt::GenericArgument>()) {
|
|
path.pop_back();
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
auto *application = getCalleeLocator(getConstraintLocator(anchor, path));
|
|
|
|
auto overload = findSelectedOverloadFor(application);
|
|
if (!(overload && overload->choice.isDecl()))
|
|
return false;
|
|
|
|
auto *choice = overload->choice.getDecl();
|
|
return choice->hasClangNode();
|
|
}
|
|
|
|
ConversionEphemeralness
|
|
ConstraintSystem::isConversionEphemeral(ConversionRestrictionKind conversion,
|
|
ConstraintLocatorBuilder locator) {
|
|
switch (conversion) {
|
|
case ConversionRestrictionKind::ArrayToPointer:
|
|
case ConversionRestrictionKind::ArrayToCPointer:
|
|
case ConversionRestrictionKind::StringToPointer:
|
|
// Always ephemeral.
|
|
return ConversionEphemeralness::Ephemeral;
|
|
case ConversionRestrictionKind::InoutToPointer:
|
|
case ConversionRestrictionKind::InoutToCPointer: {
|
|
|
|
// Ephemeral, except if the expression is a reference to a global or
|
|
// static stored variable, or a directly accessed stored property on such a
|
|
// variable.
|
|
|
|
auto isDirectlyAccessedStoredVar = [&](ValueDecl *decl) -> bool {
|
|
auto *asd = dyn_cast_or_null<AbstractStorageDecl>(decl);
|
|
if (!asd)
|
|
return false;
|
|
|
|
// Check what access strategy is used for a read-write access. It must be
|
|
// direct-to-storage in order for the conversion to be non-ephemeral.
|
|
return asd->isAccessedViaPhysicalStorage(
|
|
AccessSemantics::Ordinary, AccessKind::ReadWrite,
|
|
DC->getParentModule(), DC->getResilienceExpansion());
|
|
};
|
|
|
|
SourceRange range;
|
|
auto *argLoc = simplifyLocator(*this, getConstraintLocator(locator), range);
|
|
auto *subExpr =
|
|
castToExpr(argLoc->getAnchor())->getSemanticsProvidingExpr();
|
|
|
|
// Look through an InOutExpr if we have one. This is usually the case, but
|
|
// might not be if e.g we're applying an 'add missing &' fix.
|
|
if (auto *ioe = dyn_cast<InOutExpr>(subExpr))
|
|
subExpr = ioe->getSubExpr();
|
|
|
|
while (true) {
|
|
subExpr = subExpr->getSemanticsProvidingExpr();
|
|
|
|
// Look through force unwraps, which can be modelled as physical lvalue
|
|
// components.
|
|
if (auto *fve = dyn_cast<ForceValueExpr>(subExpr)) {
|
|
subExpr = fve->getSubExpr();
|
|
continue;
|
|
}
|
|
|
|
// Look through a member reference if it's directly accessed.
|
|
if (auto *ude = dyn_cast<UnresolvedDotExpr>(subExpr)) {
|
|
auto overload = findSelectedOverloadFor(ude);
|
|
|
|
// If we didn't find an overload, it hasn't been resolved yet.
|
|
if (!overload)
|
|
return ConversionEphemeralness::Unresolved;
|
|
|
|
// Tuple indices are always non-ephemeral.
|
|
auto *base = ude->getBase();
|
|
if (overload->choice.getKind() == OverloadChoiceKind::TupleIndex) {
|
|
subExpr = base;
|
|
continue;
|
|
}
|
|
|
|
// If we don't have a directly accessed declaration associated with the
|
|
// choice, it's ephemeral.
|
|
auto *member = overload->choice.getDeclOrNull();
|
|
if (!isDirectlyAccessedStoredVar(member))
|
|
return ConversionEphemeralness::Ephemeral;
|
|
|
|
// If we found a static member, the conversion is non-ephemeral. We can
|
|
// stop iterating as there's nothing interesting about the base.
|
|
if (member->isStatic())
|
|
return ConversionEphemeralness::NonEphemeral;
|
|
|
|
// For an instance member, the base must be an @lvalue struct type.
|
|
if (auto *lvt = simplifyType(getType(base))->getAs<LValueType>()) {
|
|
auto *nominal = lvt->getObjectType()->getAnyNominal();
|
|
if (isa_and_nonnull<StructDecl>(nominal)) {
|
|
subExpr = base;
|
|
continue;
|
|
}
|
|
}
|
|
return ConversionEphemeralness::Ephemeral;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
auto getBaseEphemeralness =
|
|
[&](ValueDecl *base) -> ConversionEphemeralness {
|
|
// We must have a base decl that's directly accessed.
|
|
if (!isDirectlyAccessedStoredVar(base))
|
|
return ConversionEphemeralness::Ephemeral;
|
|
|
|
// The base decl must either be static or global in order for it to be
|
|
// non-ephemeral.
|
|
if (base->isStatic() || base->getDeclContext()->isModuleScopeContext()) {
|
|
return ConversionEphemeralness::NonEphemeral;
|
|
} else {
|
|
return ConversionEphemeralness::Ephemeral;
|
|
}
|
|
};
|
|
|
|
// Fast path: We have a direct decl ref.
|
|
if (auto *dre = dyn_cast<DeclRefExpr>(subExpr))
|
|
return getBaseEphemeralness(dre->getDecl());
|
|
|
|
// Otherwise, try to find an overload for the base.
|
|
if (auto baseOverload = findSelectedOverloadFor(subExpr))
|
|
return getBaseEphemeralness(baseOverload->choice.getDeclOrNull());
|
|
|
|
// If we didn't find a base overload for a unresolved member or overloaded
|
|
// decl, it hasn't been resolved yet.
|
|
if (isa<UnresolvedMemberExpr>(subExpr) ||
|
|
isa<OverloadedDeclRefExpr>(subExpr))
|
|
return ConversionEphemeralness::Unresolved;
|
|
|
|
// Otherwise, we don't know what we're dealing with. Default to ephemeral.
|
|
return ConversionEphemeralness::Ephemeral;
|
|
}
|
|
case ConversionRestrictionKind::DeepEquality:
|
|
case ConversionRestrictionKind::Superclass:
|
|
case ConversionRestrictionKind::Existential:
|
|
case ConversionRestrictionKind::MetatypeToExistentialMetatype:
|
|
case ConversionRestrictionKind::ExistentialMetatypeToMetatype:
|
|
case ConversionRestrictionKind::ValueToOptional:
|
|
case ConversionRestrictionKind::OptionalToOptional:
|
|
case ConversionRestrictionKind::ClassMetatypeToAnyObject:
|
|
case ConversionRestrictionKind::ExistentialMetatypeToAnyObject:
|
|
case ConversionRestrictionKind::ProtocolMetatypeToProtocolClass:
|
|
case ConversionRestrictionKind::PointerToPointer:
|
|
case ConversionRestrictionKind::PointerToCPointer:
|
|
case ConversionRestrictionKind::ArrayUpcast:
|
|
case ConversionRestrictionKind::DictionaryUpcast:
|
|
case ConversionRestrictionKind::SetUpcast:
|
|
case ConversionRestrictionKind::HashableToAnyHashable:
|
|
case ConversionRestrictionKind::CFTollFreeBridgeToObjC:
|
|
case ConversionRestrictionKind::ObjCTollFreeBridgeToCF:
|
|
case ConversionRestrictionKind::CGFloatToDouble:
|
|
case ConversionRestrictionKind::DoubleToCGFloat:
|
|
// @_nonEphemeral has no effect on these conversions, so treat them as all
|
|
// being non-ephemeral in order to allow their passing to an @_nonEphemeral
|
|
// parameter.
|
|
return ConversionEphemeralness::NonEphemeral;
|
|
}
|
|
llvm_unreachable("invalid conversion restriction kind");
|
|
}
|
|
|
|
Expr *ConstraintSystem::buildAutoClosureExpr(Expr *expr,
|
|
FunctionType *closureType,
|
|
DeclContext *ClosureContext,
|
|
bool isDefaultWrappedValue,
|
|
bool isAsyncLetWrapper) {
|
|
auto &Context = DC->getASTContext();
|
|
bool isInDefaultArgumentContext = false;
|
|
if (auto *init = dyn_cast<Initializer>(DC)) {
|
|
auto initKind = init->getInitializerKind();
|
|
isInDefaultArgumentContext =
|
|
initKind == InitializerKind::DefaultArgument ||
|
|
(initKind == InitializerKind::PatternBinding && isDefaultWrappedValue);
|
|
}
|
|
|
|
auto info = closureType->getExtInfo();
|
|
auto newClosureType = closureType;
|
|
|
|
if (isInDefaultArgumentContext && info.isNoEscape())
|
|
newClosureType = closureType->withExtInfo(info.withNoEscape(false))
|
|
->castTo<FunctionType>();
|
|
|
|
auto *closure = new (Context)
|
|
AutoClosureExpr(expr, newClosureType, ClosureContext);
|
|
|
|
closure->setParameterList(ParameterList::createEmpty(Context));
|
|
|
|
if (isAsyncLetWrapper)
|
|
closure->setThunkKind(AutoClosureExpr::Kind::AsyncLet);
|
|
|
|
Expr *result = closure;
|
|
|
|
if (!newClosureType->isEqual(closureType)) {
|
|
assert(isInDefaultArgumentContext);
|
|
assert(newClosureType
|
|
->withExtInfo(newClosureType->getExtInfo().withNoEscape(true))
|
|
->isEqual(closureType));
|
|
result = new (Context) FunctionConversionExpr(closure, closureType);
|
|
}
|
|
|
|
cacheExprTypes(result);
|
|
return result;
|
|
}
|
|
|
|
Expr *ConstraintSystem::buildTypeErasedExpr(Expr *expr, DeclContext *dc,
|
|
Type contextualType,
|
|
ContextualTypePurpose purpose) {
|
|
auto &ctx = dc->getASTContext();
|
|
|
|
if (purpose != CTP_ReturnStmt)
|
|
return expr;
|
|
|
|
auto *decl = dyn_cast_or_null<ValueDecl>(dc->getAsDecl());
|
|
if (!decl ||
|
|
(!Context.LangOpts.hasFeature(Feature::OpaqueTypeErasure) &&
|
|
!(decl->isDynamic() || decl->getDynamicallyReplacedDecl())))
|
|
return expr;
|
|
|
|
auto *opaque = contextualType->getAs<OpaqueTypeArchetypeType>();
|
|
if (!opaque)
|
|
return expr;
|
|
|
|
auto protocols = opaque->getConformsTo();
|
|
if (protocols.size() != 1)
|
|
return expr;
|
|
|
|
auto *PD = protocols.front();
|
|
|
|
auto contextAvailability =
|
|
AvailabilityContext::forLocation(expr->getLoc(), dc);
|
|
auto refinedAvailability =
|
|
AvailabilityContext::forPlatformRange(
|
|
AvailabilityRange::alwaysAvailable(), ctx);
|
|
|
|
// The least available type eraser for the enclosing context.
|
|
Type typeEraser;
|
|
for (auto *attr : PD->getAttrs().getAttributes<TypeEraserAttr>()) {
|
|
auto eraser = attr->getResolvedType(PD);
|
|
assert(eraser && "Failed to resolve eraser type!");
|
|
|
|
auto *nominal = eraser->getAnyNominal();
|
|
auto nominalAvailability = AvailabilityContext::forDeclSignature(nominal);
|
|
|
|
if (contextAvailability.isContainedIn(nominalAvailability) &&
|
|
nominalAvailability.isContainedIn(refinedAvailability)) {
|
|
refinedAvailability = nominalAvailability;
|
|
typeEraser = eraser;
|
|
}
|
|
}
|
|
|
|
if (!typeEraser)
|
|
return expr;
|
|
|
|
auto *argList = ArgumentList::forImplicitSingle(ctx, ctx.Id_erasing, expr);
|
|
return CallExpr::createImplicit(
|
|
ctx, TypeExpr::createImplicit(typeEraser, ctx), argList);
|
|
}
|
|
|
|
/// If an UnresolvedDotExpr, SubscriptMember, etc has been resolved by the
|
|
/// constraint system, return the decl that it references.
|
|
ValueDecl *ConstraintSystem::findResolvedMemberRef(ConstraintLocator *locator) {
|
|
// See if we have a resolution for this member.
|
|
auto overload = findSelectedOverloadFor(locator);
|
|
if (!overload)
|
|
return nullptr;
|
|
|
|
// We only want to handle the simplest decl binding.
|
|
auto choice = overload->choice;
|
|
if (choice.getKind() != OverloadChoiceKind::Decl)
|
|
return nullptr;
|
|
|
|
return choice.getDecl();
|
|
}
|
|
|
|
void SyntacticElementTargetKey::dump() const { dump(llvm::errs()); }
|
|
|
|
void SyntacticElementTargetKey::dump(raw_ostream &OS) const {
|
|
switch (kind) {
|
|
case Kind::empty:
|
|
OS << "<empty>\n";
|
|
return;
|
|
|
|
case Kind::tombstone:
|
|
OS << "<tombstone>\n";
|
|
return;
|
|
|
|
case Kind::stmtCondElement:
|
|
// TODO: Implement a proper dump function for StmtConditionElement
|
|
OS << "statement condition element\n";
|
|
return;
|
|
|
|
case Kind::expr:
|
|
case Kind::closure:
|
|
storage.expr->dump(OS);
|
|
return;
|
|
|
|
case Kind::stmt:
|
|
storage.stmt->dump(OS);
|
|
return;
|
|
|
|
case Kind::pattern:
|
|
storage.pattern->dump(OS);
|
|
return;
|
|
|
|
case Kind::patternBindingEntry:
|
|
OS << "pattern binding entry " << storage.patternBindingEntry.index
|
|
<< " in\n";
|
|
storage.patternBindingEntry.patternBinding->dump(OS);
|
|
return;
|
|
|
|
case Kind::varDecl:
|
|
storage.varDecl->dump(OS);
|
|
return;
|
|
|
|
case Kind::functionRef:
|
|
OS << "<function>\n";
|
|
storage.functionRef->printContext(OS);
|
|
return;
|
|
}
|
|
llvm_unreachable("invalid statement kind");
|
|
}
|
|
|
|
/// Given a specific expression and the remnants of the failed constraint
|
|
/// system, produce a specific diagnostic.
|
|
///
|
|
/// This is guaranteed to always emit an error message.
|
|
///
|
|
void ConstraintSystem::diagnoseFailureFor(SyntacticElementTarget target) {
|
|
setPhase(ConstraintSystemPhase::Diagnostics);
|
|
|
|
SWIFT_DEFER { setPhase(ConstraintSystemPhase::Finalization); };
|
|
|
|
auto &DE = getASTContext().Diags;
|
|
|
|
// If constraint system is in invalid state always produce
|
|
// a fallback diagnostic that asks to file a bug.
|
|
if (inInvalidState()) {
|
|
DE.diagnose(target.getLoc(), diag::failed_to_produce_diagnostic);
|
|
return;
|
|
}
|
|
|
|
if (auto *wrappedVar = target.getAsUninitializedWrappedVar()) {
|
|
auto *outerWrapper = wrappedVar->getOutermostAttachedPropertyWrapper();
|
|
Type propertyType = wrappedVar->getInterfaceType();
|
|
Type wrapperType = outerWrapper->getType();
|
|
|
|
// Emit the property wrapper fallback diagnostic
|
|
wrappedVar->diagnose(diag::property_wrapper_incompatible_property,
|
|
propertyType, wrapperType);
|
|
if (auto nominal = wrapperType->getAnyNominal()) {
|
|
nominal->diagnose(diag::property_wrapper_declared_here,
|
|
nominal->getName());
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (auto expr = target.getAsExpr()) {
|
|
if (auto *assignment = dyn_cast<AssignExpr>(expr)) {
|
|
if (isa<DiscardAssignmentExpr>(assignment->getDest()))
|
|
expr = assignment->getSrc();
|
|
}
|
|
|
|
// Look through RebindSelfInConstructorExpr to avoid weird Sema issues.
|
|
if (auto *RB = dyn_cast<RebindSelfInConstructorExpr>(expr))
|
|
expr = RB->getSubExpr();
|
|
|
|
// Unresolved/Anonymous ClosureExprs are common enough that we should give
|
|
// them tailored diagnostics.
|
|
if (auto *closure = dyn_cast<ClosureExpr>(expr->getValueProvidingExpr())) {
|
|
DE.diagnose(closure->getLoc(), diag::cannot_infer_closure_type)
|
|
.highlight(closure->getSourceRange());
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Emit a poor fallback message.
|
|
auto diag = DE.diagnose(target.getLoc(), diag::failed_to_produce_diagnostic);
|
|
if (auto *expr = target.getAsExpr())
|
|
diag.highlight(expr->getSourceRange());
|
|
}
|
|
|
|
bool ConstraintSystem::isDeclUnavailable(const Decl *D,
|
|
ConstraintLocator *locator) const {
|
|
auto found = UnavailableDecls.find(std::make_pair(D, locator));
|
|
if (found != UnavailableDecls.end())
|
|
return found->second;
|
|
|
|
SourceLoc loc;
|
|
if (locator) {
|
|
if (auto anchor = locator->getAnchor())
|
|
loc = getLoc(anchor);
|
|
}
|
|
|
|
auto result = getUnsatisfiedAvailabilityConstraint(D, DC, loc).has_value();
|
|
const_cast<ConstraintSystem *>(this)->UnavailableDecls.insert(
|
|
std::make_pair(std::make_pair(D, locator), result));
|
|
return result;
|
|
}
|
|
|
|
bool ConstraintSystem::isConformanceUnavailable(ProtocolConformanceRef conformance,
|
|
ConstraintLocator *locator) const {
|
|
if (!conformance.isConcrete())
|
|
return false;
|
|
|
|
auto *concrete = conformance.getConcrete();
|
|
auto *rootConf = concrete->getRootConformance();
|
|
auto *ext = dyn_cast<ExtensionDecl>(rootConf->getDeclContext());
|
|
if (ext == nullptr)
|
|
return false;
|
|
|
|
return isDeclUnavailable(ext, locator);
|
|
}
|
|
|
|
/// If we aren't certain that we've emitted a diagnostic, emit a fallback
|
|
/// diagnostic.
|
|
void ConstraintSystem::maybeProduceFallbackDiagnostic(SourceLoc loc) const {
|
|
if (Options.contains(ConstraintSystemFlags::SuppressDiagnostics))
|
|
return;
|
|
|
|
// Before producing fatal error here, let's check if there are any "error"
|
|
// diagnostics already emitted or waiting to be emitted. Because they are
|
|
// a better indication of the problem.
|
|
ASTContext &ctx = getASTContext();
|
|
if (ctx.hadError() ||
|
|
(diagnosticTransaction && diagnosticTransaction->hasErrors()))
|
|
return;
|
|
|
|
ctx.Diags.diagnose(loc, diag::failed_to_produce_diagnostic);
|
|
}
|
|
|
|
SourceLoc constraints::getLoc(ASTNode anchor) {
|
|
if (auto *E = anchor.dyn_cast<Expr *>()) {
|
|
return E->getLoc();
|
|
} else if (auto *T = anchor.dyn_cast<TypeRepr *>()) {
|
|
return T->getLoc();
|
|
} else if (auto *V = anchor.dyn_cast<Decl *>()) {
|
|
if (auto VD = dyn_cast<VarDecl>(V))
|
|
return VD->getNameLoc();
|
|
return anchor.getStartLoc();
|
|
} else if (auto *S = anchor.dyn_cast<Stmt *>()) {
|
|
return S->getStartLoc();
|
|
} else if (auto *P = anchor.dyn_cast<Pattern *>()) {
|
|
return P->getLoc();
|
|
} else if (auto *C = anchor.dyn_cast<StmtConditionElement *>()) {
|
|
return C->getStartLoc();
|
|
} else {
|
|
auto *I = cast<CaseLabelItem *>(anchor);
|
|
return I->getStartLoc();
|
|
}
|
|
}
|
|
|
|
SourceRange constraints::getSourceRange(ASTNode anchor) {
|
|
return anchor.getSourceRange();
|
|
}
|
|
|
|
static std::optional<Requirement>
|
|
getRequirement(ConstraintSystem &cs, ConstraintLocator *reqLocator) {
|
|
ArrayRef<LocatorPathElt> path = reqLocator->getPath();
|
|
|
|
// If we have something like ... -> type req # -> pack element #, we're
|
|
// solving a requirement of the form T : P where T is a type parameter pack
|
|
if (!path.empty() && path.back().is<LocatorPathElt::PackElement>())
|
|
path = path.drop_back();
|
|
|
|
if (path.empty())
|
|
return std::nullopt;
|
|
|
|
auto reqLoc = path.back().getAs<LocatorPathElt::AnyRequirement>();
|
|
if (!reqLoc)
|
|
return std::nullopt;
|
|
|
|
if (reqLoc->isConditionalRequirement()) {
|
|
auto conformanceRef =
|
|
reqLocator->findLast<LocatorPathElt::ConformanceRequirement>();
|
|
assert(conformanceRef && "Invalid locator for a conditional requirement");
|
|
|
|
auto conformance = conformanceRef->getConformance();
|
|
return conformance->getConditionalRequirements()[reqLoc->getIndex()];
|
|
}
|
|
|
|
if (auto openedGeneric =
|
|
reqLocator->findLast<LocatorPathElt::OpenedGeneric>()) {
|
|
auto signature = openedGeneric->getSignature();
|
|
return signature.getRequirements()[reqLoc->getIndex()];
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
static std::optional<std::pair<GenericTypeParamType *, RequirementKind>>
|
|
getRequirementInfo(ConstraintSystem &cs, ConstraintLocator *reqLocator) {
|
|
auto requirement = getRequirement(cs, reqLocator);
|
|
if (!requirement)
|
|
return std::nullopt;
|
|
|
|
auto *GP = requirement->getFirstType()->getAs<GenericTypeParamType>();
|
|
if (!GP)
|
|
return std::nullopt;
|
|
|
|
auto path = reqLocator->getPath();
|
|
auto iter = path.rbegin();
|
|
|
|
auto openedGeneric =
|
|
reqLocator->findLast<LocatorPathElt::OpenedGeneric>(iter);
|
|
assert(openedGeneric);
|
|
(void)openedGeneric;
|
|
|
|
auto newPath = path.drop_back(iter - path.rbegin() + 1);
|
|
auto *baseLoc = cs.getConstraintLocator(reqLocator->getAnchor(), newPath);
|
|
|
|
auto substitutions = cs.getOpenedTypes(baseLoc);
|
|
auto replacement =
|
|
llvm::find_if(substitutions, [&GP](const OpenedType &entry) {
|
|
auto *typeVar = entry.second;
|
|
return typeVar->getImpl().getGenericParameter() == GP;
|
|
});
|
|
|
|
if (replacement == substitutions.end())
|
|
return std::nullopt;
|
|
|
|
auto *repr = cs.getRepresentative(replacement->second);
|
|
return std::make_pair(repr->getImpl().getGenericParameter(),
|
|
requirement->getKind());
|
|
}
|
|
|
|
bool ConstraintSystem::isFixedRequirement(ConstraintLocator *reqLocator,
|
|
Type requirementTy) {
|
|
if (auto reqInfo = getRequirementInfo(*this, reqLocator)) {
|
|
auto *GP = reqInfo->first;
|
|
auto reqKind = static_cast<unsigned>(reqInfo->second);
|
|
return FixedRequirements.count(
|
|
std::make_tuple(GP, reqKind, requirementTy.getPointer()));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ConstraintSystem::recordFixedRequirement(ConstraintLocator *reqLocator,
|
|
Type requirementTy) {
|
|
if (auto reqInfo = getRequirementInfo(*this, reqLocator)) {
|
|
auto *GP = reqInfo->first;
|
|
auto reqKind = static_cast<unsigned>(reqInfo->second);
|
|
recordFixedRequirement(GP, reqKind, requirementTy);
|
|
}
|
|
}
|
|
|
|
void ConstraintSystem::recordFixedRequirement(GenericTypeParamType *GP,
|
|
unsigned reqKind,
|
|
Type requirementTy) {
|
|
bool inserted = FixedRequirements.insert(
|
|
std::make_tuple(GP, reqKind, requirementTy.getPointer())).second;
|
|
if (inserted) {
|
|
if (solverState) {
|
|
recordChange(SolverTrail::Change::AddedFixedRequirement(
|
|
GP, reqKind, requirementTy));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConstraintSystem::removeFixedRequirement(GenericTypeParamType *GP,
|
|
unsigned reqKind,
|
|
Type requirementTy) {
|
|
auto key = std::make_tuple(GP, reqKind, requirementTy.getPointer());
|
|
bool erased = FixedRequirements.erase(key);
|
|
ASSERT(erased);
|
|
}
|
|
|
|
bool ConstraintSystem::isReadOnlyKeyPathComponent(
|
|
const AbstractStorageDecl *storage, SourceLoc referenceLoc) {
|
|
// See whether key paths can store to this component. (Key paths don't
|
|
// get any special power from being formed in certain contexts, such
|
|
// as the ability to assign to `let`s in initialization contexts, so
|
|
// we pass null for the DC to `isSettable` here.)
|
|
if (!getASTContext().isSwiftVersionAtLeast(5)) {
|
|
// As a source-compatibility measure, continue to allow
|
|
// WritableKeyPaths to be formed in the same conditions we did
|
|
// in previous releases even if we should not be able to set
|
|
// the value in this context.
|
|
if (!storage->isSettableInSwift(DC)) {
|
|
// A non-settable component makes the key path read-only, unless
|
|
// a reference-writable component shows up later.
|
|
return true;
|
|
}
|
|
} else if (!storage->isSettableInSwift(nullptr) ||
|
|
!storage->isSetterAccessibleFrom(DC)) {
|
|
// A non-settable component makes the key path read-only, unless
|
|
// a reference-writable component shows up later.
|
|
return true;
|
|
}
|
|
|
|
// If the setter is unavailable, then the keypath ought to be read-only
|
|
// in this context.
|
|
if (auto setter = storage->getOpaqueAccessor(AccessorKind::Set)) {
|
|
if (getUnsatisfiedAvailabilityConstraint(setter, DC, referenceLoc))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ConstraintSystem::isArgumentGenericFunction(Type argType, Expr *argExpr) {
|
|
// Only makes sense if the argument type involves type variables somehow.
|
|
if (!argType->hasTypeVariable())
|
|
return false;
|
|
|
|
// Have we bound an overload for the argument already?
|
|
if (argExpr) {
|
|
if (auto selectedOverload = findSelectedOverloadFor(argExpr)) {
|
|
// If the overload choice is a generic function, then we have a generic
|
|
// function reference.
|
|
auto choice = selectedOverload->choice;
|
|
if (auto func =
|
|
dyn_cast_or_null<AbstractFunctionDecl>(choice.getDeclOrNull())) {
|
|
if (func->isGeneric())
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// We might have a type variable referring to an overload set.
|
|
auto argTypeVar = argType->getAs<TypeVariableType>();
|
|
if (!argTypeVar)
|
|
return false;
|
|
|
|
auto disjunction = getUnboundBindOverloadDisjunction(argTypeVar);
|
|
if (!disjunction)
|
|
return false;
|
|
|
|
for (auto constraint : disjunction->getNestedConstraints()) {
|
|
auto *decl = constraint->getOverloadChoice().getDeclOrNull();
|
|
if (!decl)
|
|
continue;
|
|
|
|
if (auto func = dyn_cast<AbstractFunctionDecl>(decl))
|
|
if (func->isGeneric())
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
ProtocolConformanceRef
|
|
ConstraintSystem::lookupConformance(Type type, ProtocolDecl *protocol) {
|
|
auto cacheKey = std::make_pair(type.getPointer(), protocol);
|
|
|
|
auto cachedConformance = Conformances.find(cacheKey);
|
|
if (cachedConformance != Conformances.end())
|
|
return cachedConformance->second;
|
|
|
|
auto conformance =
|
|
swift::lookupConformance(type, protocol, /*allowMissing=*/true);
|
|
Conformances[cacheKey] = conformance;
|
|
return conformance;
|
|
}
|
|
|
|
std::pair<bool, std::optional<KeyPathCapability>>
|
|
ConstraintSystem::inferKeyPathLiteralCapability(TypeVariableType *keyPathType) {
|
|
auto *typeLocator = keyPathType->getImpl().getLocator();
|
|
assert(typeLocator->isLastElement<LocatorPathElt::KeyPathType>());
|
|
|
|
auto *keyPath = castToExpr<KeyPathExpr>(typeLocator->getAnchor());
|
|
return inferKeyPathLiteralCapability(keyPath);
|
|
}
|
|
|
|
std::pair<bool, std::optional<KeyPathCapability>>
|
|
ConstraintSystem::inferKeyPathLiteralCapability(KeyPathExpr *keyPath) {
|
|
bool didOptionalChain = false;
|
|
bool isSendable = true;
|
|
|
|
auto fail = []() -> std::pair<bool, std::optional<KeyPathCapability>> {
|
|
return std::make_pair(false, std::nullopt);
|
|
};
|
|
|
|
auto delay = []() -> std::pair<bool, std::optional<KeyPathCapability>> {
|
|
return std::make_pair(true, std::nullopt);
|
|
};
|
|
|
|
auto success =
|
|
[](KeyPathMutability mutability,
|
|
bool isSendable) -> std::pair<bool, std::optional<KeyPathCapability>> {
|
|
KeyPathCapability capability(mutability, isSendable);
|
|
return std::make_pair(true, capability);
|
|
};
|
|
|
|
if (keyPath->hasSingleInvalidComponent())
|
|
return fail();
|
|
|
|
// If root is determined to be a hole it means that none of the components
|
|
// are resolvable and key path is not viable.
|
|
auto rootTy =
|
|
getFixedTypeRecursive(getKeyPathRootType(keyPath), /*wantRValue=*/false);
|
|
if (rootTy->isPlaceholder())
|
|
return fail();
|
|
|
|
auto isKnownSendability =
|
|
[&](const KeyPathExpr::Component &component) -> bool {
|
|
if (!Context.LangOpts.hasFeature(Feature::InferSendableFromCaptures))
|
|
return true;
|
|
|
|
// Key path is sendable only when all of its captures are sendable.
|
|
if (auto *args = component.getArgs()) {
|
|
auto *sendable = Context.getProtocol(KnownProtocolKind::Sendable);
|
|
|
|
for (const auto &arg : *args) {
|
|
// No need to check more or delay since we already know
|
|
// that the type is not Sendable.
|
|
if (!isSendable)
|
|
break;
|
|
|
|
auto argTy = simplifyType(getType(arg.getExpr()));
|
|
|
|
// Sendability cannot be determined until the argument
|
|
// is fully resolved.
|
|
if (argTy->hasTypeVariable())
|
|
return false;
|
|
|
|
auto conformance = lookupConformance(argTy, sendable);
|
|
isSendable &= bool(conformance) && !conformance.hasMissingConformance();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
auto mutability = KeyPathMutability::Writable;
|
|
for (unsigned i : indices(keyPath->getComponents())) {
|
|
auto &component = keyPath->getComponents()[i];
|
|
|
|
switch (component.getKind()) {
|
|
case KeyPathExpr::Component::Kind::Invalid:
|
|
case KeyPathExpr::Component::Kind::Identity:
|
|
break;
|
|
|
|
case KeyPathExpr::Component::Kind::CodeCompletion: {
|
|
return fail();
|
|
}
|
|
|
|
case KeyPathExpr::Component::Kind::Apply:
|
|
case KeyPathExpr::Component::Kind::UnresolvedApply: {
|
|
if (!isKnownSendability(component))
|
|
return delay();
|
|
break;
|
|
}
|
|
case KeyPathExpr::Component::Kind::UnresolvedSubscript:
|
|
case KeyPathExpr::Component::Kind::Subscript: {
|
|
if (!isKnownSendability(component))
|
|
return delay();
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
case KeyPathExpr::Component::Kind::Member:
|
|
case KeyPathExpr::Component::Kind::UnresolvedMember: {
|
|
auto *componentLoc =
|
|
getConstraintLocator(keyPath, LocatorPathElt::KeyPathComponent(i));
|
|
auto *calleeLoc = getCalleeLocator(componentLoc);
|
|
auto overload = findSelectedOverloadFor(calleeLoc);
|
|
if (!overload) {
|
|
// If overload cannot be found because member is missing,
|
|
// that's a failure.
|
|
if (hasFixFor(componentLoc, FixKind::DefineMemberBasedOnUse))
|
|
return fail();
|
|
|
|
return delay();
|
|
}
|
|
|
|
// tuple elements do not change the capability of the key path
|
|
auto choice = overload->choice;
|
|
if (choice.getKind() == OverloadChoiceKind::TupleIndex) {
|
|
continue;
|
|
}
|
|
|
|
// Discarded unsupported non-decl member lookups.
|
|
if (!choice.isDecl())
|
|
return fail();
|
|
|
|
if (hasFixFor(componentLoc, FixKind::AllowInvalidRefInKeyPath) ||
|
|
hasFixFor(componentLoc, FixKind::UnwrapOptionalBase) ||
|
|
hasFixFor(componentLoc,
|
|
FixKind::UnwrapOptionalBaseWithOptionalResult))
|
|
return fail();
|
|
|
|
auto *storageDecl = dyn_cast<AbstractStorageDecl>(choice.getDecl());
|
|
if (!isa<AbstractFunctionDecl>(choice.getDecl()) && !storageDecl) {
|
|
return fail();
|
|
}
|
|
|
|
// Shared switch logic for actor isolation.
|
|
switch (getActorIsolation(choice.getDecl())) {
|
|
case ActorIsolation::Unspecified:
|
|
case ActorIsolation::Nonisolated:
|
|
case ActorIsolation::CallerIsolationInheriting:
|
|
case ActorIsolation::NonisolatedUnsafe:
|
|
break;
|
|
|
|
case ActorIsolation::Erased:
|
|
llvm_unreachable("storage cannot have opaque isolation");
|
|
|
|
// A reference to an actor-isolated state makes key path non-Sendable.
|
|
case ActorIsolation::ActorInstance:
|
|
case ActorIsolation::GlobalActor:
|
|
isSendable = false;
|
|
break;
|
|
}
|
|
|
|
if (storageDecl) {
|
|
if (isReadOnlyKeyPathComponent(storageDecl, component.getLoc())) {
|
|
mutability = KeyPathMutability::ReadOnly;
|
|
continue;
|
|
}
|
|
|
|
// A nonmutating setter indicates a reference-writable base.
|
|
if (!storageDecl->isSetterMutating()) {
|
|
mutability = KeyPathMutability::ReferenceWritable;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Otherwise, the key path maintains its current capability.
|
|
break;
|
|
}
|
|
|
|
case KeyPathExpr::Component::Kind::OptionalChain:
|
|
didOptionalChain = true;
|
|
break;
|
|
|
|
case KeyPathExpr::Component::Kind::OptionalForce:
|
|
// Forcing an optional preserves its lvalue-ness.
|
|
break;
|
|
|
|
case KeyPathExpr::Component::Kind::OptionalWrap:
|
|
// An optional chain should already have been recorded.
|
|
assert(didOptionalChain);
|
|
break;
|
|
|
|
case KeyPathExpr::Component::Kind::TupleElement:
|
|
llvm_unreachable("not implemented");
|
|
break;
|
|
|
|
case KeyPathExpr::Component::Kind::DictionaryKey:
|
|
llvm_unreachable("DictionaryKey only valid in #keyPath");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Optional chains force the entire key path to be read-only.
|
|
if (didOptionalChain)
|
|
mutability = KeyPathMutability::ReadOnly;
|
|
|
|
return success(mutability, isSendable);
|
|
}
|
|
|
|
TypeVarBindingProducer::TypeVarBindingProducer(BindingSet &bindings)
|
|
: BindingProducer(bindings.getConstraintSystem(),
|
|
bindings.getTypeVariable()->getImpl().getLocator()),
|
|
TypeVar(bindings.getTypeVariable()), 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 (const auto &entry : bindings.Defaults) {
|
|
auto *constraint = entry.second;
|
|
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 &info : bindings.Literals) {
|
|
const auto &literal = info.second;
|
|
|
|
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 (const auto &entry : bindings.Defaults) {
|
|
auto *constraint = entry.second;
|
|
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;
|
|
}
|
|
|
|
ValueDecl *constraints::getOverloadChoiceDecl(Constraint *choice) {
|
|
if (choice->getKind() != ConstraintKind::BindOverload)
|
|
return nullptr;
|
|
return choice->getOverloadChoice().getDeclOrNull();
|
|
}
|
|
|
|
bool constraints::isOperatorDisjunction(Constraint *disjunction) {
|
|
assert(disjunction->getKind() == ConstraintKind::Disjunction);
|
|
|
|
auto choices = disjunction->getNestedConstraints();
|
|
assert(!choices.empty());
|
|
|
|
auto *decl = getOverloadChoiceDecl(choices.front());
|
|
return decl ? decl->isOperator() : false;
|
|
}
|
|
|
|
ASTNode constraints::findAsyncNode(ClosureExpr *closure) {
|
|
auto *body = closure->getBody();
|
|
if (!body)
|
|
return ASTNode();
|
|
return body->findAsyncNode();
|
|
}
|
|
|
|
void constraints::dumpAnchor(ASTNode anchor, SourceManager *SM,
|
|
raw_ostream &out) {
|
|
if (auto *expr = anchor.dyn_cast<Expr *>()) {
|
|
out << Expr::getKindName(expr->getKind());
|
|
if (SM) {
|
|
out << '@';
|
|
expr->getLoc().print(out, *SM);
|
|
}
|
|
} else if (auto *pattern = anchor.dyn_cast<Pattern *>()) {
|
|
out << Pattern::getKindName(pattern->getKind()) << "Pattern";
|
|
if (SM) {
|
|
out << '@';
|
|
pattern->getLoc().print(out, *SM);
|
|
}
|
|
} else if (auto *decl = anchor.dyn_cast<Decl *>()) {
|
|
if (auto *VD = dyn_cast<ValueDecl>(decl)) {
|
|
VD->dumpRef(out);
|
|
} else {
|
|
out << "<<" << Decl::getKindName(decl->getKind()) << ">>";
|
|
if (SM) {
|
|
out << "@";
|
|
decl->getLoc().print(out, *SM);
|
|
}
|
|
}
|
|
}
|
|
// TODO(diagnostics): Implement the rest of the cases.
|
|
}
|
|
|
|
bool constraints::isResultBuilderMethodReference(ASTContext &ctx,
|
|
UnresolvedDotExpr *UDE) {
|
|
if (!(UDE && UDE->isImplicit()))
|
|
return false;
|
|
|
|
SmallVector<Identifier, 5> builderMethods(
|
|
{ctx.Id_buildBlock, ctx.Id_buildExpression, ctx.Id_buildPartialBlock,
|
|
ctx.Id_buildFinalResult, ctx.Id_buildIf});
|
|
|
|
return llvm::any_of(builderMethods, [&](const Identifier &methodId) {
|
|
return UDE->getName().compare(DeclNameRef(methodId)) == 0;
|
|
});
|
|
}
|