Files
swift-mirror/lib/Sema/CSGen.cpp
Doug Gregor 8c34d57874 [Strict memory safety] Eliminate spurious warnings with synthesized Codable
When synthesizing code for Codable conformances involving unsafe types,
make sure to wrap the resulting expressions in "unsafe" when strict memory safety is enabled.

Tweak the warning-emission logic to suppress warnings about spurious
"unsafe" expressions when the compiler generated the "unsafe" itself,
so we don't spam the developer with warnings they can't fix. Also make
the checking for other suppression considerations safe when there are
no source locations, eliminating a potential assertion.

Fixes rdar://153665692.
2025-07-10 09:12:49 -07:00

5288 lines
202 KiB
C++

//===--- CSGen.cpp - Constraint Generator ---------------------------------===//
//
// 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 constraint generation for the type checker.
//
//===----------------------------------------------------------------------===//
#include "TypeCheckConcurrency.h"
#include "TypeCheckDecl.h"
#include "TypeCheckMacros.h"
#include "TypeCheckType.h"
#include "TypeChecker.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/ConformanceLookup.h"
#include "swift/AST/Expr.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/Assertions.h"
#include "swift/Sema/ConstraintGraph.h"
#include "swift/Sema/ConstraintSystem.h"
#include "swift/Sema/IDETypeChecking.h"
#include "swift/Subsystems.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include <utility>
using namespace swift;
using namespace swift::constraints;
static bool isArithmeticOperatorDecl(ValueDecl *vd) {
return vd && vd->getBaseIdentifier().isArithmeticOperator();
}
static bool mergeRepresentativeEquivalenceClasses(ConstraintSystem &CS,
TypeVariableType* tyvar1,
TypeVariableType* tyvar2) {
if (tyvar1 && tyvar2) {
auto rep1 = CS.getRepresentative(tyvar1);
auto rep2 = CS.getRepresentative(tyvar2);
if (rep1 != rep2) {
auto fixedType2 = CS.getFixedType(rep2);
// If the there exists fixed type associated with the second
// type variable, and we simply merge two types together it would
// mean that portion of the constraint graph previously associated
// with that (second) variable is going to be disconnected from its
// new equivalence class, which is going to lead to incorrect solutions,
// so we need to make sure to re-bind fixed to the new representative.
if (fixedType2) {
CS.addConstraint(ConstraintKind::Bind, fixedType2, rep1,
rep1->getImpl().getLocator());
}
CS.mergeEquivalenceClasses(rep1, rep2, /*updateWorkList*/ false);
return true;
}
}
return false;
}
namespace {
/// Internal struct for tracking information about types within a series
/// of "linked" expressions. (Such as a chain of binary operator invocations.)
struct LinkedTypeInfo {
bool hasLiteral = false;
llvm::SmallSet<TypeBase*, 16> collectedTypes;
llvm::SmallVector<BinaryExpr *, 4> binaryExprs;
};
/// Walks an expression sub-tree, and collects information about expressions
/// whose types are mutually dependent upon one another.
class LinkedExprCollector : public ASTWalker {
llvm::SmallVectorImpl<Expr*> &LinkedExprs;
public:
LinkedExprCollector(llvm::SmallVectorImpl<Expr *> &linkedExprs)
: LinkedExprs(linkedExprs) {}
MacroWalking getMacroWalkingBehavior() const override {
return MacroWalking::Arguments;
}
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
if (isa<ClosureExpr>(expr))
return Action::SkipNode(expr);
// Store top-level binary exprs for further analysis.
if (isa<BinaryExpr>(expr) ||
// Literal exprs are contextually typed, so store them off as well.
isa<LiteralExpr>(expr) ||
// We'd like to look at the elements of arrays and dictionaries.
isa<ArrayExpr>(expr) ||
isa<DictionaryExpr>(expr) ||
// assignment expression can involve anonymous closure parameters
// as source and destination, so it's beneficial for diagnostics if
// we look at the assignment.
isa<AssignExpr>(expr)) {
LinkedExprs.push_back(expr);
return Action::SkipNode(expr);
}
return Action::Continue(expr);
}
/// Ignore statements.
PreWalkResult<Stmt *> walkToStmtPre(Stmt *stmt) override {
return Action::SkipNode(stmt);
}
/// Ignore declarations.
PreWalkAction walkToDeclPre(Decl *decl) override {
return Action::SkipNode();
}
/// Ignore patterns.
PreWalkResult<Pattern *> walkToPatternPre(Pattern *pat) override {
return Action::SkipNode(pat);
}
/// Ignore types.
PreWalkAction walkToTypeReprPre(TypeRepr *T) override {
return Action::SkipNode();
}
};
/// Given a collection of "linked" expressions, analyzes them for
/// commonalities regarding their types. This will help us compute a
/// "best common type" from the expression types.
class LinkedExprAnalyzer : public ASTWalker {
LinkedTypeInfo &LTI;
ConstraintSystem &CS;
public:
LinkedExprAnalyzer(LinkedTypeInfo &lti, ConstraintSystem &cs) :
LTI(lti), CS(cs) {}
MacroWalking getMacroWalkingBehavior() const override {
return MacroWalking::Arguments;
}
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
if (isa<LiteralExpr>(expr)) {
LTI.hasLiteral = true;
return Action::SkipNode(expr);
}
if (isa<CollectionExpr>(expr)) {
return Action::Continue(expr);
}
if (auto UDE = dyn_cast<UnresolvedDotExpr>(expr)) {
if (CS.hasType(UDE))
LTI.collectedTypes.insert(CS.getType(UDE).getPointer());
// Don't recurse into the base expression.
return Action::SkipNode(expr);
}
if (isa<ClosureExpr>(expr)) {
return Action::SkipNode(expr);
}
if (auto FVE = dyn_cast<ForceValueExpr>(expr)) {
LTI.collectedTypes.insert(CS.getType(FVE).getPointer());
return Action::SkipNode(expr);
}
if (auto DRE = dyn_cast<DeclRefExpr>(expr)) {
if (isa<VarDecl>(DRE->getDecl())) {
if (CS.hasType(DRE)) {
LTI.collectedTypes.insert(CS.getType(DRE).getPointer());
}
return Action::SkipNode(expr);
}
}
// In the case of a function application, we would have already captured
// the return type during constraint generation, so there's no use in
// looking any further.
if (isa<ApplyExpr>(expr) &&
!(isa<BinaryExpr>(expr) || isa<PrefixUnaryExpr>(expr) ||
isa<PostfixUnaryExpr>(expr))) {
return Action::SkipNode(expr);
}
if (auto *binaryExpr = dyn_cast<BinaryExpr>(expr)) {
LTI.binaryExprs.push_back(binaryExpr);
}
if (auto favoredType = CS.getFavoredType(expr)) {
LTI.collectedTypes.insert(favoredType);
return Action::SkipNode(expr);
}
// Optimize branches of a conditional expression separately.
if (auto IE = dyn_cast<TernaryExpr>(expr)) {
CS.optimizeConstraints(IE->getCondExpr());
CS.optimizeConstraints(IE->getThenExpr());
CS.optimizeConstraints(IE->getElseExpr());
return Action::SkipNode(expr);
}
// For exprs of a tuple, avoid favoring. (We need to allow for cases like
// (Int, Int32).)
if (isa<TupleExpr>(expr)) {
return Action::SkipNode(expr);
}
// Coercion exprs have a rigid type, so there's no use in gathering info
// about them.
if (auto *coercion = dyn_cast<CoerceExpr>(expr)) {
// Let's not collect information about types initialized by
// coercions just like we don't for regular initializer calls,
// because that might lead to overly eager type variable merging.
if (!coercion->isLiteralInit())
LTI.collectedTypes.insert(CS.getType(expr).getPointer());
return Action::SkipNode(expr);
}
// Don't walk into subscript expressions - to do so would risk factoring
// the index expression into edge contraction. (We don't want to do this
// if the index expression is a literal type that differs from the return
// type of the subscript operation.)
if (isa<SubscriptExpr>(expr) || isa<DynamicLookupExpr>(expr)) {
return Action::SkipNode(expr);
}
// Don't walk into unresolved member expressions - we avoid merging type
// variables inside UnresolvedMemberExpr and those outside, since they
// should be allowed to behave independently in CS.
if (isa<UnresolvedMemberExpr>(expr)) {
return Action::SkipNode(expr);
}
return Action::Continue(expr);
}
/// Ignore statements.
PreWalkResult<Stmt *> walkToStmtPre(Stmt *stmt) override {
return Action::SkipNode(stmt);
}
/// Ignore declarations.
PreWalkAction walkToDeclPre(Decl *decl) override {
return Action::SkipNode();
}
/// Ignore patterns.
PreWalkResult<Pattern *> walkToPatternPre(Pattern *pat) override {
return Action::SkipNode(pat);
}
/// Ignore types.
PreWalkAction walkToTypeReprPre(TypeRepr *T) override {
return Action::SkipNode();
}
};
/// For a given expression, given information that is global to the
/// expression, attempt to derive a favored type for it.
void computeFavoredTypeForExpr(Expr *expr, ConstraintSystem &CS) {
LinkedTypeInfo lti;
expr->walk(LinkedExprAnalyzer(lti, CS));
// Check whether we can proceed with favoring.
if (llvm::any_of(lti.binaryExprs, [](const BinaryExpr *op) {
auto *ODRE = dyn_cast<OverloadedDeclRefExpr>(op->getFn());
if (!ODRE)
return false;
// Attempting to favor based on operand types is wrong for
// nil-coalescing operator.
auto identifier = ODRE->getDecls().front()->getBaseIdentifier();
return identifier.isNilCoalescingOperator();
})) {
return;
}
if (lti.collectedTypes.size() == 1) {
// TODO: Compute the BCT.
// It's only useful to favor the type instead of
// binding it directly to arguments/result types,
// which means in case it has been miscalculated
// solver can still make progress.
auto favoredTy = (*lti.collectedTypes.begin())->getWithoutSpecifierType();
CS.setFavoredType(expr, favoredTy.getPointer());
// If we have a chain of identical binop expressions with homogeneous
// argument types, we can directly simplify the associated constraint
// graph.
auto simplifyBinOpExprTyVars = [&]() {
// Don't attempt to do linking if there are
// literals intermingled with other inferred types.
if (lti.hasLiteral)
return;
for (auto binExp1 : lti.binaryExprs) {
for (auto binExp2 : lti.binaryExprs) {
if (binExp1 == binExp2)
continue;
auto fnTy1 = CS.getType(binExp1)->getAs<TypeVariableType>();
auto fnTy2 = CS.getType(binExp2)->getAs<TypeVariableType>();
if (!(fnTy1 && fnTy2))
return;
auto ODR1 = dyn_cast<OverloadedDeclRefExpr>(binExp1->getFn());
auto ODR2 = dyn_cast<OverloadedDeclRefExpr>(binExp2->getFn());
if (!(ODR1 && ODR2))
return;
// TODO: We currently limit this optimization to known arithmetic
// operators, but we should be able to broaden this out to
// logical operators as well.
if (!isArithmeticOperatorDecl(ODR1->getDecls()[0]))
return;
if (ODR1->getDecls()[0]->getBaseName() !=
ODR2->getDecls()[0]->getBaseName())
return;
// All things equal, we can merge the tyvars for the function
// types.
auto rep1 = CS.getRepresentative(fnTy1);
auto rep2 = CS.getRepresentative(fnTy2);
if (rep1 != rep2) {
CS.mergeEquivalenceClasses(rep1, rep2,
/*updateWorkList*/ false);
}
auto odTy1 = CS.getType(ODR1)->getAs<TypeVariableType>();
auto odTy2 = CS.getType(ODR2)->getAs<TypeVariableType>();
if (odTy1 && odTy2) {
auto odRep1 = CS.getRepresentative(odTy1);
auto odRep2 = CS.getRepresentative(odTy2);
// Since we'll be choosing the same overload, we can merge
// the overload tyvar as well.
if (odRep1 != odRep2)
CS.mergeEquivalenceClasses(odRep1, odRep2,
/*updateWorkList*/ false);
}
}
}
};
simplifyBinOpExprTyVars();
}
}
/// Determine whether the given parameter type and argument should be
/// "favored" because they match exactly.
bool isFavoredParamAndArg(ConstraintSystem &CS, Type paramTy, Type argTy,
Type otherArgTy = Type()) {
// Determine the argument type.
argTy = argTy->getWithoutSpecifierType();
// Do the types match exactly?
if (paramTy->isEqual(argTy))
return true;
// Don't favor narrowing conversions.
if (argTy->isDouble() && paramTy->isCGFloat())
return false;
llvm::SmallSetVector<ProtocolDecl *, 2> literalProtos;
if (auto argTypeVar = argTy->getAs<TypeVariableType>()) {
auto constraints = CS.getConstraintGraph().gatherNearbyConstraints(
argTypeVar,
[](Constraint *constraint) {
return constraint->getKind() == ConstraintKind::LiteralConformsTo;
});
for (auto constraint : constraints) {
literalProtos.insert(constraint->getProtocol());
}
}
// Dig out the second argument type.
if (otherArgTy)
otherArgTy = otherArgTy->getWithoutSpecifierType();
for (auto literalProto : literalProtos) {
// If there is another, concrete argument, check whether it's type
// conforms to the literal protocol and test against it directly.
// This helps to avoid 'widening' the favored type to the default type for
// the literal.
if (otherArgTy && otherArgTy->getAnyNominal()) {
if (otherArgTy->isEqual(paramTy) &&
CS.lookupConformance(otherArgTy, literalProto)) {
return true;
}
} else if (Type defaultType =
TypeChecker::getDefaultType(literalProto, CS.DC)) {
// If there is a default type for the literal protocol, check whether
// it is the same as the parameter type.
// Check whether there is a default type to compare against.
if (paramTy->isEqual(defaultType) ||
(defaultType->isDouble() && paramTy->isCGFloat()))
return true;
}
}
return false;
}
/// Favor certain overloads in a call based on some basic analysis
/// of the overload set and call arguments.
///
/// \param expr The application.
/// \param isFavored Determine whether the given overload is favored, passing
/// it the "effective" overload type when it's being called.
/// \param mustConsider If provided, a function to detect the presence of
/// overloads which inhibit any overload from being favored.
void favorCallOverloads(ApplyExpr *expr,
ConstraintSystem &CS,
llvm::function_ref<bool(ValueDecl *, Type)> isFavored,
std::function<bool(ValueDecl *)>
mustConsider = nullptr) {
// Find the type variable associated with the function, if any.
auto tyvarType = CS.getType(expr->getFn())->getAs<TypeVariableType>();
if (!tyvarType || CS.getFixedType(tyvarType))
return;
// This type variable is only currently associated with the function
// being applied, and the only constraint attached to it should
// be the disjunction constraint for the overload group.
auto disjunction = CS.getUnboundBindOverloadDisjunction(tyvarType);
if (!disjunction)
return;
// Find the favored constraints and mark them.
SmallVector<Constraint *, 4> newlyFavoredConstraints;
unsigned numFavoredConstraints = 0;
Constraint *firstFavored = nullptr;
for (auto constraint : disjunction->getNestedConstraints()) {
auto *decl = constraint->getOverloadChoice().getDeclOrNull();
if (!decl)
continue;
if (mustConsider && mustConsider(decl)) {
// Roll back any constraints we favored.
for (auto favored : newlyFavoredConstraints)
favored->setFavored(false);
return;
}
Type overloadType = CS.getEffectiveOverloadType(
constraint->getLocator(), constraint->getOverloadChoice(),
/*allowMembers=*/true, CS.DC);
if (!overloadType)
continue;
if (!CS.isDeclUnavailable(decl, constraint->getLocator()) &&
!decl->getAttrs().hasAttribute<DisfavoredOverloadAttr>() &&
isFavored(decl, overloadType)) {
// If we might need to roll back the favored constraints, keep
// track of those we are favoring.
if (mustConsider && !constraint->isFavored())
newlyFavoredConstraints.push_back(constraint);
constraint->setFavored();
++numFavoredConstraints;
if (!firstFavored)
firstFavored = constraint;
}
}
// If there was one favored constraint, set the favored type based on its
// result type.
if (numFavoredConstraints == 1) {
auto overloadChoice = firstFavored->getOverloadChoice();
auto overloadType = CS.getEffectiveOverloadType(
firstFavored->getLocator(), overloadChoice, /*allowMembers=*/true,
CS.DC);
auto resultType = overloadType->castTo<AnyFunctionType>()->getResult();
if (!resultType->hasTypeParameter())
CS.setFavoredType(expr, resultType.getPointer());
}
}
/// Return a pair, containing the total parameter count of a function, coupled
/// with the number of non-default parameters.
std::pair<size_t, size_t> getParamCount(ValueDecl *VD) {
auto fTy = VD->getInterfaceType()->castTo<AnyFunctionType>();
size_t nOperands = fTy->getParams().size();
size_t nNoDefault = 0;
if (auto AFD = dyn_cast<AbstractFunctionDecl>(VD)) {
assert(!AFD->hasImplicitSelfDecl());
for (auto param : *AFD->getParameters()) {
if (!param->isDefaultArgument())
++nNoDefault;
}
} else {
nNoDefault = nOperands;
}
return { nOperands, nNoDefault };
}
bool hasContextuallyFavorableResultType(AnyFunctionType *choice,
Type contextualTy) {
// No restrictions of what result could be.
if (!contextualTy)
return true;
auto resultTy = choice->getResult();
// Result type of the call matches expected contextual type.
return contextualTy->isEqual(resultTy);
}
/// Favor unary operator constraints where we have exact matches
/// for the operand and contextual type.
void favorMatchingUnaryOperators(ApplyExpr *expr,
ConstraintSystem &CS) {
auto *unaryArg = expr->getArgs()->getUnaryExpr();
assert(unaryArg);
// Determine whether the given declaration is favored.
auto isFavoredDecl = [&](ValueDecl *value, Type type) -> bool {
auto fnTy = type->getAs<AnyFunctionType>();
if (!fnTy)
return false;
auto params = fnTy->getParams();
if (params.size() != 1)
return false;
auto paramTy = params[0].getPlainType();
auto argTy = CS.getType(unaryArg);
// There are no CGFloat overloads on some of the unary operators, so
// in order to preserve current behavior, let's not favor overloads
// which would result in conversion from CGFloat to Double; otherwise
// it would lead to ambiguities.
if (argTy->isCGFloat() && paramTy->isDouble())
return false;
return isFavoredParamAndArg(CS, paramTy, argTy) &&
hasContextuallyFavorableResultType(
fnTy,
CS.getContextualType(expr, /*forConstraint=*/false));
};
favorCallOverloads(expr, CS, isFavoredDecl);
}
void favorMatchingOverloadExprs(ApplyExpr *expr,
ConstraintSystem &CS) {
// Find the argument type.
size_t nArgs = expr->getArgs()->size();
auto fnExpr = expr->getFn();
auto mustConsiderVariadicGenericOverloads = [&](ValueDecl *overload) {
if (overload->getAttrs().hasAttribute<DisfavoredOverloadAttr>())
return false;
auto genericContext = overload->getAsGenericContext();
if (!genericContext)
return false;
auto *GPL = genericContext->getGenericParams();
if (!GPL)
return false;
return llvm::any_of(GPL->getParams(),
[&](const GenericTypeParamDecl *GP) {
return GP->isParameterPack();
});
};
// Check to ensure that we have an OverloadedDeclRef, and that we're not
// favoring multiple overload constraints. (Otherwise, in this case
// favoring is useless.
if (auto ODR = dyn_cast<OverloadedDeclRefExpr>(fnExpr)) {
bool haveMultipleApplicableOverloads = false;
for (auto VD : ODR->getDecls()) {
if (VD->getInterfaceType()->is<AnyFunctionType>()) {
auto nParams = getParamCount(VD);
if (nArgs == nParams.first) {
if (haveMultipleApplicableOverloads) {
return;
} else {
haveMultipleApplicableOverloads = true;
}
}
}
}
// Determine whether the given declaration is favored.
auto isFavoredDecl = [&](ValueDecl *value, Type type) -> bool {
// We want to consider all options for calls that might contain the code
// completion location, as missing arguments after the completion
// location are valid (since it might be that they just haven't been
// written yet).
if (CS.isForCodeCompletion())
return false;
if (!type->is<AnyFunctionType>())
return false;
auto paramCount = getParamCount(value);
return nArgs == paramCount.first ||
nArgs == paramCount.second;
};
favorCallOverloads(expr, CS, isFavoredDecl,
mustConsiderVariadicGenericOverloads);
}
// We only currently perform favoring for unary args.
auto *unaryArg = expr->getArgs()->getUnlabeledUnaryExpr();
if (!unaryArg)
return;
if (auto favoredTy = CS.getFavoredType(unaryArg)) {
// Determine whether the given declaration is favored.
auto isFavoredDecl = [&](ValueDecl *value, Type type) -> bool {
auto fnTy = type->getAs<AnyFunctionType>();
if (!fnTy || fnTy->getParams().size() != 1)
return false;
return favoredTy->isEqual(fnTy->getParams()[0].getPlainType());
};
// This is a hack to ensure we always consider the protocol requirement
// itself when calling something that has a default implementation in an
// extension. Otherwise, the extension method might be favored if we're
// inside an extension context, since any archetypes in the parameter
// list could match exactly.
auto mustConsider = [&](ValueDecl *value) -> bool {
return isa<ProtocolDecl>(value->getDeclContext()) ||
mustConsiderVariadicGenericOverloads(value);
};
favorCallOverloads(expr, CS, isFavoredDecl, mustConsider);
}
}
/// Favor binary operator constraints where we have exact matches
/// for the operands and contextual type.
void favorMatchingBinaryOperators(ApplyExpr *expr, ConstraintSystem &CS) {
// If we're generating constraints for a binary operator application,
// there are two special situations to consider:
// 1. If the type checker has any newly created functions with the
// operator's name. If it does, the overloads were created after the
// associated overloaded id expression was created, and we'll need to
// add a new disjunction constraint for the new set of overloads.
// 2. If any component argument expressions (nested or otherwise) are
// literals, we can favor operator overloads whose argument types are
// identical to the literal type, or whose return types are identical
// to any contextual type associated with the application expression.
// Find the argument types.
auto *args = expr->getArgs();
auto *lhs = args->getExpr(0);
auto *rhs = args->getExpr(1);
auto firstArgTy = CS.getType(lhs);
auto secondArgTy = CS.getType(rhs);
auto isOptionalWithMatchingObjectType = [](Type optional,
Type object) -> bool {
if (auto objTy = optional->getRValueType()->getOptionalObjectType())
return objTy->getRValueType()->isEqual(object->getRValueType());
return false;
};
auto isPotentialForcingOpportunity = [&](Type first, Type second) -> bool {
return isOptionalWithMatchingObjectType(first, second) ||
isOptionalWithMatchingObjectType(second, first);
};
// Determine whether the given declaration is favored.
auto isFavoredDecl = [&](ValueDecl *value, Type type) -> bool {
auto fnTy = type->getAs<AnyFunctionType>();
if (!fnTy)
return false;
auto firstFavoredTy = CS.getFavoredType(lhs);
auto secondFavoredTy = CS.getFavoredType(rhs);
auto favoredExprTy = CS.getFavoredType(expr);
if (isArithmeticOperatorDecl(value)) {
// If the parent has been favored on the way down, propagate that
// information to its children.
// TODO: This is only valid for arithmetic expressions.
if (!firstFavoredTy) {
CS.setFavoredType(lhs, favoredExprTy);
firstFavoredTy = favoredExprTy;
}
if (!secondFavoredTy) {
CS.setFavoredType(rhs, favoredExprTy);
secondFavoredTy = favoredExprTy;
}
}
auto params = fnTy->getParams();
if (params.size() != 2)
return false;
auto firstParamTy = params[0].getOldType();
auto secondParamTy = params[1].getOldType();
auto contextualTy = CS.getContextualType(expr, /*forConstraint=*/false);
// Avoid favoring overloads that would require narrowing conversion
// to match the arguments.
{
if (firstArgTy->isDouble() && firstParamTy->isCGFloat())
return false;
if (secondArgTy->isDouble() && secondParamTy->isCGFloat())
return false;
}
return (isFavoredParamAndArg(CS, firstParamTy, firstArgTy, secondArgTy) ||
isFavoredParamAndArg(CS, secondParamTy, secondArgTy,
firstArgTy)) &&
firstParamTy->isEqual(secondParamTy) &&
!isPotentialForcingOpportunity(firstArgTy, secondArgTy) &&
hasContextuallyFavorableResultType(fnTy, contextualTy);
};
favorCallOverloads(expr, CS, isFavoredDecl);
}
/// If \p expr is a call and that call contains the code completion token,
/// add the expressions of all arguments after the code completion token to
/// \p ignoredArguments.
/// Otherwise, returns an empty vector.
/// Assumes that we are solving for code completion.
void getArgumentsAfterCodeCompletionToken(
Expr *expr, ConstraintSystem &CS,
SmallVectorImpl<Expr *> &ignoredArguments) {
assert(CS.isForCodeCompletion());
/// Don't ignore the rhs argument if the code completion token is the lhs of
/// an operator call. Main use case is the implicit `<complete> ~= $match`
/// call created for pattern matching, in which we need to type-check
/// `$match` to get a contextual type for `<complete>`
if (isa<BinaryExpr>(expr)) {
return;
}
auto args = expr->getArgs();
auto argInfo = getCompletionArgInfo(expr, CS);
if (!args || !argInfo) {
return;
}
for (auto argIndex : indices(*args)) {
if (argInfo->isBefore(argIndex)) {
ignoredArguments.push_back(args->get(argIndex).getExpr());
}
}
}
class ConstraintOptimizer : public ASTWalker {
ConstraintSystem &CS;
public:
ConstraintOptimizer(ConstraintSystem &cs) :
CS(cs) {}
MacroWalking getMacroWalkingBehavior() const override {
return MacroWalking::Arguments;
}
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
if (CS.isArgumentIgnoredForCodeCompletion(expr)) {
return Action::SkipNode(expr);
}
if (auto applyExpr = dyn_cast<ApplyExpr>(expr)) {
if (isa<PrefixUnaryExpr>(applyExpr) ||
isa<PostfixUnaryExpr>(applyExpr)) {
favorMatchingUnaryOperators(applyExpr, CS);
} else if (isa<BinaryExpr>(applyExpr)) {
favorMatchingBinaryOperators(applyExpr, CS);
} else {
favorMatchingOverloadExprs(applyExpr, CS);
}
}
// If the paren expr has a favored type, and the subExpr doesn't,
// propagate downwards. Otherwise, propagate upwards.
if (auto parenExpr = dyn_cast<ParenExpr>(expr)) {
if (!CS.getFavoredType(parenExpr->getSubExpr())) {
CS.setFavoredType(parenExpr->getSubExpr(),
CS.getFavoredType(parenExpr));
} else if (!CS.getFavoredType(parenExpr)) {
CS.setFavoredType(parenExpr,
CS.getFavoredType(parenExpr->getSubExpr()));
}
}
if (isa<ClosureExpr>(expr))
return Action::SkipNode(expr);
return Action::Continue(expr);
}
/// Ignore statements.
PreWalkResult<Stmt *> walkToStmtPre(Stmt *stmt) override {
return Action::SkipNode(stmt);
}
/// Ignore declarations.
PreWalkAction walkToDeclPre(Decl *decl) override {
return Action::SkipNode();
}
};
} // end anonymous namespace
void TypeVarRefCollector::inferTypeVars(Decl *D) {
// We're only interested in VarDecls.
if (!isa_and_nonnull<VarDecl>(D))
return;
auto ty = CS.getTypeIfAvailable(D);
if (!ty)
return;
SmallPtrSet<TypeVariableType *, 4> typeVars;
ty->getTypeVariables(typeVars);
TypeVars.insert(typeVars.begin(), typeVars.end());
}
void TypeVarRefCollector::inferTypeVars(PackExpansionExpr *E) {
auto expansionType = CS.getType(E)->castTo<PackExpansionType>();
SmallPtrSet<TypeVariableType *, 4> referencedVars;
expansionType->getTypeVariables(referencedVars);
TypeVars.insert(referencedVars.begin(), referencedVars.end());
}
ASTWalker::PreWalkResult<Expr *>
TypeVarRefCollector::walkToExprPre(Expr *expr) {
if (isa<ClosureExpr>(expr))
DCDepth += 1;
if (auto *DRE = dyn_cast<DeclRefExpr>(expr))
inferTypeVars(DRE->getDecl());
// FIXME: We can see UnresolvedDeclRefExprs here because we don't walk into
// patterns when running preCheckTarget, since we don't resolve patterns
// until CSGen. We ought to consider moving pattern resolution into
// pre-checking, which would allow us to pre-check patterns normally.
if (auto *declRef = dyn_cast<UnresolvedDeclRefExpr>(expr)) {
auto name = declRef->getName();
auto loc = declRef->getLoc();
if (name.isSimpleName() && loc.isValid()) {
auto *SF = CS.DC->getParentSourceFile();
auto *D = ASTScope::lookupSingleLocalDecl(SF, name.getFullName(), loc);
inferTypeVars(D);
}
}
if (auto *packElement = getAsExpr<PackElementExpr>(expr)) {
// If expansion hasn't been established yet, it means that pack expansion
// appears inside of this closure.
if (auto *outerExpansion = CS.getPackElementExpansion(packElement))
inferTypeVars(outerExpansion);
}
return Action::Continue(expr);
}
ASTWalker::PostWalkResult<Expr *>
TypeVarRefCollector::walkToExprPost(Expr *expr) {
if (isa<ClosureExpr>(expr))
DCDepth -= 1;
return Action::Continue(expr);
}
ASTWalker::PreWalkResult<Stmt *>
TypeVarRefCollector::walkToStmtPre(Stmt *stmt) {
// If we have a return without any intermediate DeclContexts in a ClosureExpr,
// we need to include any type variables in the closure's result type, since
// the conjunction will generate constraints using that type. We don't need to
// connect to returns in e.g nested closures since we'll connect those when we
// generate constraints for those closures. We also don't need to bother if
// we're generating constraints for the closure itself, since we'll connect
// the conjunction to the closure type variable itself.
if (auto *CE = dyn_cast<ClosureExpr>(DC)) {
if (isa<ReturnStmt>(stmt) && DCDepth == 0 &&
!Locator->directlyAt<ClosureExpr>()) {
SmallPtrSet<TypeVariableType *, 4> typeVars;
CS.getClosureType(CE)->getResult()->getTypeVariables(typeVars);
TypeVars.insert(typeVars.begin(), typeVars.end());
}
}
return Action::Continue(stmt);
}
namespace {
class ConstraintGenerator : public ExprVisitor<ConstraintGenerator, Type> {
ConstraintSystem &CS;
DeclContext *CurDC;
ConstraintSystemPhase CurrPhase;
/// A map from each UnresolvedMemberExpr to the respective (implicit) base
/// found during our walk.
llvm::MapVector<UnresolvedMemberExpr *, Type> UnresolvedBaseTypes;
/// A stack of pack expansions that can open pack elements.
llvm::SmallVector<PackExpansionExpr *, 1> OuterExpansions;
/// Returns false and emits the specified diagnostic if the member reference
/// base is a nil literal. Returns true otherwise.
bool isValidBaseOfMemberRef(Expr *base, Diag<> diagnostic) {
if (auto nilLiteral = dyn_cast<NilLiteralExpr>(base)) {
CS.getASTContext().Diags.diagnose(nilLiteral->getLoc(), diagnostic);
return false;
}
return true;
}
/// Retrieves a matching set of function params for an argument list.
void getMatchingParams(ArgumentList *argList,
SmallVectorImpl<AnyFunctionType::Param> &result) {
for (auto arg : *argList) {
ParameterTypeFlags flags;
auto ty = CS.getType(arg.getExpr());
if (arg.isInOut()) {
ty = ty->getInOutObjectType();
flags = flags.withInOut(true);
}
if (arg.isCompileTimeLiteral()) {
flags = flags.withCompileTimeLiteral(true);
}
result.emplace_back(ty, arg.getLabel(), flags);
}
}
/// If the provided type is a tuple, decomposes it into a matching set of
/// function params. Otherwise produces a single parameter of the type.
void decomposeTuple(Type ty,
SmallVectorImpl<AnyFunctionType::Param> &result) {
switch (ty->getKind()) {
case TypeKind::Tuple: {
auto tupleTy = cast<TupleType>(ty.getPointer());
for (auto &elt : tupleTy->getElements())
result.emplace_back(elt.getType(), elt.getName());
return;
}
default:
result.emplace_back(ty, Identifier());
}
}
/// Add constraints for a reference to a named member of the given
/// base type, and return the type of such a reference.
Type addMemberRefConstraints(Expr *expr, Expr *base, DeclNameRef name,
FunctionRefInfo functionRefInfo,
ArrayRef<ValueDecl *> outerAlternatives) {
// The base must have a member of the given name, such that accessing
// that member through the base returns a value convertible to the type
// of this expression.
auto baseTy = CS.getType(base);
if (isa<ErrorExpr>(base)) {
return CS.createTypeVariable(
CS.getConstraintLocator(expr, ConstraintLocator::Member),
TVO_CanBindToHole);
}
unsigned options = (TVO_CanBindToLValue |
TVO_CanBindToNoEscape);
if (!OuterExpansions.empty())
options |= TVO_CanBindToPack;
auto tv = CS.createTypeVariable(
CS.getConstraintLocator(expr, ConstraintLocator::Member),
options);
SmallVector<OverloadChoice, 4> outerChoices;
for (auto decl : outerAlternatives) {
outerChoices.push_back(OverloadChoice(Type(), decl, functionRefInfo));
}
CS.addValueMemberConstraint(
baseTy, name, tv, CurDC, functionRefInfo, outerChoices,
CS.getConstraintLocator(expr, ConstraintLocator::Member));
return tv;
}
/// Add constraints for a reference to a specific member of the given
/// base type, and return the type of such a reference.
Type addMemberRefConstraints(Expr *expr, Expr *base, ValueDecl *decl,
FunctionRefInfo functionRefInfo) {
// If we're referring to an invalid declaration, fail.
if (!decl)
return nullptr;
if (decl->isInvalid())
return nullptr;
auto memberLocator =
CS.getConstraintLocator(expr, ConstraintLocator::Member);
auto tv = CS.createTypeVariable(memberLocator,
TVO_CanBindToLValue | TVO_CanBindToNoEscape);
OverloadChoice choice =
OverloadChoice(CS.getType(base), decl, functionRefInfo);
auto locator = CS.getConstraintLocator(expr, ConstraintLocator::Member);
CS.addBindOverloadConstraint(tv, choice, locator, CurDC);
return tv;
}
/// Add constraints for a subscript operation.
Type addSubscriptConstraints(
Expr *anchor, Type baseTy, ValueDecl *declOrNull, ArgumentList *argList,
ConstraintLocator *locator = nullptr,
SmallVectorImpl<TypeVariableType *> *addedTypeVars = nullptr) {
// Locators used in this expression.
if (locator == nullptr)
locator = CS.getConstraintLocator(anchor);
auto fnLocator =
CS.getConstraintLocator(locator,
ConstraintLocator::ApplyFunction);
auto memberLocator =
CS.getConstraintLocator(locator,
ConstraintLocator::SubscriptMember);
auto resultLocator =
CS.getConstraintLocator(locator,
ConstraintLocator::FunctionResult);
CS.associateArgumentList(memberLocator, argList);
Type outputTy;
// For an integer subscript expression on an array slice type, instead of
// introducing a new type variable we can easily obtain the element type.
if (isa<SubscriptExpr>(anchor)) {
auto isLValueBase = false;
auto baseObjTy = baseTy;
if (baseObjTy->is<LValueType>()) {
isLValueBase = true;
baseObjTy = baseObjTy->getWithoutSpecifierType();
}
if (auto elementTy = baseObjTy->isArrayType()) {
if (auto arraySliceTy =
dyn_cast<ArraySliceType>(baseObjTy.getPointer())) {
baseObjTy = arraySliceTy->getDesugaredType();
}
if (argList->isUnlabeledUnary() &&
isa<IntegerLiteralExpr>(argList->getExpr(0))) {
outputTy = elementTy;
if (isLValueBase)
outputTy = LValueType::get(outputTy);
}
} else if (auto dictTy = CS.isDictionaryType(baseObjTy)) {
auto keyTy = dictTy->first;
auto valueTy = dictTy->second;
if (argList->isUnlabeledUnary()) {
auto argTy = CS.getType(argList->getExpr(0));
if (isFavoredParamAndArg(CS, keyTy, argTy)) {
outputTy = OptionalType::get(valueTy);
if (isLValueBase)
outputTy = LValueType::get(outputTy);
}
}
}
}
if (outputTy.isNull()) {
outputTy = CS.createTypeVariable(resultLocator,
TVO_CanBindToLValue | TVO_CanBindToNoEscape);
if (addedTypeVars)
addedTypeVars->push_back(outputTy->castTo<TypeVariableType>());
}
// FIXME: This can only happen when diagnostics successfully type-checked
// sub-expression of the subscript and mutated AST, but under normal
// circumstances subscript should never have InOutExpr as a direct child
// until type checking is complete and expression is re-written.
// Proper fix for such situation requires preventing diagnostics from
// re-writing AST after successful type checking of the sub-expressions.
if (auto inoutTy = baseTy->getAs<InOutType>()) {
baseTy = LValueType::get(inoutTy->getObjectType());
}
// Add the member constraint for a subscript declaration.
// FIXME: weak name!
auto memberTy = CS.createTypeVariable(
memberLocator, TVO_CanBindToLValue | TVO_CanBindToNoEscape);
if (addedTypeVars)
addedTypeVars->push_back(memberTy);
// FIXME: synthesizeMaterializeForSet() wants to statically dispatch to
// a known subscript here. This might be cleaner if we split off a new
// UnresolvedSubscriptExpr from SubscriptExpr.
if (auto decl = declOrNull) {
OverloadChoice choice = OverloadChoice(
baseTy, decl, FunctionRefInfo::doubleBaseNameApply());
CS.addBindOverloadConstraint(memberTy, choice, memberLocator,
CurDC);
} else {
CS.addValueMemberConstraint(baseTy, DeclNameRef::createSubscript(),
memberTy, CurDC,
FunctionRefInfo::doubleBaseNameApply(),
/*outerAlternatives=*/{}, memberLocator);
}
SmallVector<AnyFunctionType::Param, 8> params;
getMatchingParams(argList, params);
// Add the constraint that the index expression's type be convertible
// to the input type of the subscript operator.
CS.addApplicationConstraint(FunctionType::get(params, outputTy), memberTy,
/*trailingClosureMatching=*/std::nullopt,
CurDC, fnLocator);
Type fixedOutputType =
CS.getFixedTypeRecursive(outputTy, /*wantRValue=*/false);
if (!fixedOutputType->isTypeVariableOrMember()) {
CS.setFavoredType(anchor, fixedOutputType.getPointer());
outputTy = fixedOutputType;
}
return outputTy;
}
/// Add constraints for argument resolution for `UnresolvedApply` key path
/// component kind.
Type addApplyConstraints(
Expr *anchor, Type memberTy, ArgumentList *argList,
ConstraintLocator *memberComponentLoc,
ConstraintLocator *applyComponentLoc,
SmallVectorImpl<TypeVariableType *> *addedTypeVars = nullptr) {
// Locators used in this expression.
assert(applyComponentLoc != nullptr && "applyComponentLoc should not be null");
auto fnLocator = CS.getConstraintLocator(
applyComponentLoc, ConstraintLocator::ApplyFunction);
auto fnResultLocator = CS.getConstraintLocator(
applyComponentLoc, ConstraintLocator::FunctionResult);
CS.associateArgumentList(applyComponentLoc, argList);
Type outputTy = CS.createTypeVariable(
fnResultLocator, TVO_CanBindToLValue | TVO_CanBindToNoEscape);
if (addedTypeVars)
addedTypeVars->push_back(outputTy->castTo<TypeVariableType>());
SmallVector<AnyFunctionType::Param, 8> params;
getMatchingParams(argList, params);
SourceLoc loc =
CurDC->getAsDecl() ? CurDC->getAsDecl()->getLoc() : SourceLoc();
for (auto index : indices(params)) {
const auto &param = params[index];
CS.verifyThatArgumentIsHashable(index, param.getParameterType(),
memberComponentLoc, loc);
}
// Add the constraint that the index expression's type be convertible
// to the input type of the subscript operator.
CS.addApplicationConstraint(FunctionType::get(params, outputTy), memberTy,
/*trailingClosureMatching=*/std::nullopt,
CurDC, fnLocator);
return outputTy;
}
Type openPackElement(Type packType, ConstraintLocator *locator,
PackExpansionExpr *packElementEnvironment) {
if (!packElementEnvironment) {
return CS.createTypeVariable(locator,
TVO_CanBindToHole | TVO_CanBindToNoEscape);
}
// The type of a PackElementExpr is the opened pack element archetype
// of the pack reference.
OpenPackElementType openPackElement(CS, locator, packElementEnvironment);
return openPackElement(packType, /*packRepr*/ nullptr);
}
public:
ConstraintGenerator(ConstraintSystem &CS, DeclContext *DC)
: CS(CS), CurDC(DC ? DC : CS.DC), CurrPhase(CS.getPhase()) {
// Although constraint system is initialized in `constraint
// generation` phase, we have to set it here manually because e.g.
// result builders could generate constraints for its body
// in the middle of the solving.
CS.setPhase(ConstraintSystemPhase::ConstraintGeneration);
// Pick up the saved stack of pack expansions so we can continue
// to handle pack element references inside the closure body.
if (auto *ACE = dyn_cast<AbstractClosureExpr>(CurDC)) {
OuterExpansions = CS.getCapturedExpansions(ACE);
}
}
virtual ~ConstraintGenerator() {
CS.setPhase(CurrPhase);
}
ConstraintSystem &getConstraintSystem() const { return CS; }
void pushPackExpansionExpr(PackExpansionExpr *expr) {
OuterExpansions.push_back(expr);
SmallVector<ASTNode, 2> expandedPacks;
collectExpandedPacks(expr, expandedPacks);
for (auto pack : expandedPacks) {
if (auto *elementExpr = getAsExpr<PackElementExpr>(pack))
CS.recordPackElementExpansion(elementExpr, expr);
}
auto *patternLoc = CS.getConstraintLocator(
expr, ConstraintLocator::PackExpansionPattern);
auto patternType = CS.createTypeVariable(
patternLoc,
TVO_CanBindToPack | TVO_CanBindToNoEscape | TVO_CanBindToHole);
auto *shapeLoc =
CS.getConstraintLocator(expr, ConstraintLocator::PackShape);
auto *shapeTypeVar = CS.createTypeVariable(
shapeLoc, TVO_CanBindToPack | TVO_CanBindToHole);
auto expansionType = PackExpansionType::get(patternType, shapeTypeVar);
CS.setType(expr, expansionType);
}
/// Records a fix for an invalid AST node, and returns a potential hole
/// type variable for it.
Type recordInvalidNode(ASTNode node) {
CS.recordFix(
IgnoreInvalidASTNode::create(CS, CS.getConstraintLocator(node)));
return CS.createTypeVariable(CS.getConstraintLocator(node),
TVO_CanBindToHole);
}
virtual Type visitErrorExpr(ErrorExpr *E) {
return recordInvalidNode(E);
}
virtual Type visitCodeCompletionExpr(CodeCompletionExpr *E) {
CS.Options |= ConstraintSystemFlags::SuppressDiagnostics;
auto locator = CS.getConstraintLocator(E);
return CS.createTypeVariable(locator, TVO_CanBindToLValue |
TVO_CanBindToNoEscape |
TVO_CanBindToHole);
}
Type visitNilLiteralExpr(NilLiteralExpr *expr) {
auto literalTy = visitLiteralExpr(expr);
// Allow `nil` to be a hole so we can diagnose it via a fix
// if it turns out that there is no contextual information.
if (auto *typeVar = literalTy->getAs<TypeVariableType>())
CS.recordPotentialHole(typeVar);
return literalTy;
}
Type visitFloatLiteralExpr(FloatLiteralExpr *expr) {
auto &ctx = CS.getASTContext();
// Get the _MaxBuiltinFloatType decl, or look for it if it's not cached.
auto maxFloatTypeDecl = ctx.get_MaxBuiltinFloatTypeDecl();
if (!maxFloatTypeDecl ||
!maxFloatTypeDecl->getDeclaredInterfaceType()->is<BuiltinFloatType>()) {
ctx.Diags.diagnose(expr->getLoc(), diag::no_MaxBuiltinFloatType_found);
return nullptr;
}
return visitLiteralExpr(expr);
}
Type visitLiteralExpr(LiteralExpr *expr) {
// If the expression has already been assigned a type; just use that type.
if (expr->getType())
return expr->getType();
auto protocol = TypeChecker::getLiteralProtocol(CS.getASTContext(), expr);
if (!protocol)
return nullptr;
auto tv = CS.createTypeVariable(CS.getConstraintLocator(expr),
TVO_PrefersSubtypeBinding |
TVO_CanBindToNoEscape);
CS.addConstraint(ConstraintKind::LiteralConformsTo, tv,
protocol->getDeclaredInterfaceType(),
CS.getConstraintLocator(expr));
return tv;
}
Type
visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *expr) {
// Dig out the ExpressibleByStringInterpolation protocol.
auto &ctx = CS.getASTContext();
auto interpolationProto = TypeChecker::getProtocol(
ctx, expr->getLoc(),
KnownProtocolKind::ExpressibleByStringInterpolation);
if (!interpolationProto) {
ctx.Diags.diagnose(expr->getStartLoc(),
diag::interpolation_missing_proto);
return nullptr;
}
// The type of the expression must conform to the
// ExpressibleByStringInterpolation protocol.
auto locator = CS.getConstraintLocator(expr);
auto tv = CS.createTypeVariable(locator,
TVO_PrefersSubtypeBinding |
TVO_CanBindToNoEscape);
CS.addConstraint(ConstraintKind::LiteralConformsTo, tv,
interpolationProto->getDeclaredInterfaceType(),
locator);
if (auto appendingExpr = expr->getAppendingExpr()) {
auto associatedTypeDecl = interpolationProto->getAssociatedType(
ctx.Id_StringInterpolation);
if (associatedTypeDecl == nullptr) {
ctx.Diags.diagnose(expr->getStartLoc(),
diag::interpolation_broken_proto);
return nullptr;
}
auto interpolationTV =
CS.createTypeVariable(locator, TVO_CanBindToNoEscape);
auto interpolationType =
DependentMemberType::get(tv, associatedTypeDecl);
CS.addConstraint(ConstraintKind::Equal, interpolationTV,
interpolationType, locator);
auto appendingExprType = CS.getType(appendingExpr);
auto appendingLocator = CS.getConstraintLocator(appendingExpr);
SmallVector<TypeVariableType *, 2> referencedVars;
if (auto *tap = getAsExpr<TapExpr>(appendingExpr)) {
// Collect all of the variable references that appear
// in the tap body, otherwise tap expression is going
// to get disconnected from the context.
if (auto *body = tap->getBody()) {
TypeVarRefCollector refCollector(
CS, tap->getVar()->getDeclContext(), locator);
body->walk(refCollector);
auto vars = refCollector.getTypeVars();
referencedVars.append(vars.begin(), vars.end());
}
}
// Must be Conversion; if it's Equal, then in semi-rare cases, the
// interpolation temporary variable cannot be @lvalue.
CS.addUnsolvedConstraint(Constraint::create(
CS, ConstraintKind::Conversion, appendingExprType, interpolationTV,
appendingLocator, referencedVars));
}
return tv;
}
Type visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *expr) {
#ifdef SWIFT_BUILD_SWIFT_SYNTAX
auto &ctx = CS.getASTContext();
if (ctx.LangOpts.hasFeature(Feature::BuiltinMacros)) {
auto kind = MagicIdentifierLiteralExpr::getKindString(expr->getKind())
.drop_front();
auto protocol =
TypeChecker::getLiteralProtocol(CS.getASTContext(), expr);
if (!protocol)
return Type();
auto macroIdent = ctx.getIdentifier(kind);
auto macros = lookupMacros(Identifier(), macroIdent,
FunctionRefInfo::unappliedBaseName(),
MacroRole::Expression);
if (!macros.empty()) {
// Introduce an overload set for the macro reference.
auto locator = CS.getConstraintLocator(expr);
auto macroRefType = Type(CS.createTypeVariable(locator, 0));
CS.addOverloadSet(macroRefType, macros, CurDC, locator);
// FIXME: Can this be encoded in the macro definition somehow?
CS.addConstraint(ConstraintKind::LiteralConformsTo, macroRefType,
protocol->getDeclaredInterfaceType(),
CS.getConstraintLocator(expr));
return macroRefType;
}
}
// Fall through to use old implementation.
#endif
switch (expr->getKind()) {
// Magic pointer identifiers are of type UnsafeMutableRawPointer.
#define MAGIC_POINTER_IDENTIFIER(NAME, STRING) \
case MagicIdentifierLiteralExpr::NAME:
#include "swift/AST/MagicIdentifierKinds.def"
{
auto &ctx = CS.getASTContext();
if (TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc()))
return nullptr;
return ctx.getUnsafeRawPointerType();
}
default:
// Others are actual literals and should be handled like any literal.
return visitLiteralExpr(expr);
}
llvm_unreachable("Unhandled MagicIdentifierLiteralExpr in switch.");
}
Type visitObjectLiteralExpr(ObjectLiteralExpr *expr) {
auto *exprLoc = CS.getConstraintLocator(expr);
CS.associateArgumentList(exprLoc, expr->getArgs());
// If the expression has already been assigned a type; just use that type.
if (expr->getType())
return expr->getType();
auto &ctx = CS.getASTContext();
auto &de = ctx.Diags;
auto protocol = TypeChecker::getLiteralProtocol(ctx, expr);
if (!protocol) {
de.diagnose(expr->getLoc(), diag::use_unknown_object_literal_protocol,
expr->getLiteralKindPlainName());
return nullptr;
}
auto witnessType = CS.createTypeVariable(
exprLoc, TVO_PrefersSubtypeBinding | TVO_CanBindToNoEscape |
TVO_CanBindToHole);
CS.addConstraint(ConstraintKind::LiteralConformsTo, witnessType,
protocol->getDeclaredInterfaceType(), exprLoc);
// The arguments are required to be argument-convertible to the
// idealized parameter type of the initializer, which generally
// simplifies the first label (e.g. "colorLiteralRed:") by stripping
// all the redundant stuff about literals (leaving e.g. "red:").
// Constraint application will quietly rewrite the type of 'args' to
// use the right labels before forming the call to the initializer.
auto constrName = TypeChecker::getObjectLiteralConstructorName(ctx, expr);
assert(constrName);
auto *constr = dyn_cast_or_null<ConstructorDecl>(
protocol->getSingleRequirement(constrName));
if (!constr) {
de.diagnose(protocol, diag::object_literal_broken_proto);
return nullptr;
}
auto *memberLoc =
CS.getConstraintLocator(expr, ConstraintLocator::ConstructorMember);
auto *fnLoc =
CS.getConstraintLocator(expr, ConstraintLocator::ApplyFunction);
auto *memberTypeLoc = CS.getConstraintLocator(
fnLoc, LocatorPathElt::ConstructorMemberType());
auto *memberType =
CS.createTypeVariable(memberTypeLoc, TVO_CanBindToNoEscape);
CS.addValueMemberConstraint(MetatypeType::get(witnessType, ctx),
DeclNameRef(constrName), memberType, CurDC,
FunctionRefInfo::doubleBaseNameApply(), {},
memberLoc);
SmallVector<AnyFunctionType::Param, 8> params;
getMatchingParams(expr->getArgs(), params);
auto resultType = CS.createTypeVariable(
CS.getConstraintLocator(expr, ConstraintLocator::FunctionResult),
TVO_CanBindToNoEscape);
CS.addApplicationConstraint(
FunctionType::get(params, resultType), memberType,
/*trailingClosureMatching=*/std::nullopt, CurDC, fnLoc);
if (constr->isFailable())
return OptionalType::get(witnessType);
return witnessType;
}
Type visitRegexLiteralExpr(RegexLiteralExpr *E) {
// Retrieve the computed Regex type from the compiler regex library.
auto ty = E->getRegexType();
if (!ty)
return recordInvalidNode(E);
return ty;
}
PackExpansionExpr *getParentPackExpansionExpr(Expr *E) const {
auto *current = E;
while (auto *parent = CS.getParentExpr(current)) {
if (auto *expansion = dyn_cast<PackExpansionExpr>(parent)) {
return expansion;
}
current = parent;
}
return nullptr;
}
Type visitDeclRefExpr(DeclRefExpr *E) {
auto locator = CS.getConstraintLocator(E);
auto invalidateReference = [&]() -> Type {
auto *hole = CS.createTypeVariable(locator, TVO_CanBindToHole);
(void)CS.recordFix(AllowRefToInvalidDecl::create(CS, locator));
CS.setType(E, hole);
return hole;
};
Type knownType;
if (auto *VD = dyn_cast<VarDecl>(E->getDecl())) {
knownType = CS.getTypeIfAvailable(VD);
if (!knownType)
knownType = VD->getTypeInContext();
if (knownType) {
// An out-of-scope type variable(s) could appear the type of
// a declaration only in diagnostic mode when invalid variable
// declaration is recursively referenced inside of a multi-statement
// closure located somewhere within its initializer e.g.:
// `let x = [<call>] { ... print(x) }`. It happens because the
// variable assumes the result type of its initializer unless
// its specified explicitly.
if (isa<ClosureExpr>(CurDC) && knownType->hasTypeVariable()) {
if (knownType.findIf([&](Type type) {
auto *typeVar = type->getAs<TypeVariableType>();
if (!typeVar || CS.getFixedType(typeVar))
return false;
return !CS.isActiveTypeVariable(typeVar);
}))
return invalidateReference();
}
// If the known type has an error, bail out.
if (knownType->hasError()) {
return invalidateReference();
}
// value packs cannot be referenced without `each` immediately
// preceding them.
if (auto *expansionType = knownType->getAs<PackExpansionType>()) {
if (!isExpr<PackElementExpr>(CS.getParentExpr(E))) {
auto packType = expansionType->getPatternType();
(void)CS.recordFix(
IgnoreMissingEachKeyword::create(CS, packType, locator));
return openPackElement(packType, locator,
getParentPackExpansionExpr(E));
}
}
if (!knownType->hasPlaceholder()) {
// Set the favored type for this expression to the known type.
CS.setFavoredType(E, knownType.getPointer());
}
}
}
// If declaration is invalid, let's turn it into a potential hole
// and keep generating constraints.
// For code completion, we still resolve the overload and replace error
// types inside the function decl with placeholders
// (in getTypeOfReference) so we can match non-error param types.
if (!knownType && E->getDecl()->isInvalid() &&
!CS.isForCodeCompletion()) {
return invalidateReference();
}
unsigned options = (TVO_CanBindToLValue |
TVO_CanBindToNoEscape);
if (!OuterExpansions.empty())
options |= TVO_CanBindToPack;
// Create an overload choice referencing this declaration and immediately
// resolve it. This records the overload for use later.
auto tv = CS.createTypeVariable(locator, options);
OverloadChoice choice =
OverloadChoice(Type(), E->getDecl(), E->getFunctionRefInfo());
CS.resolveOverload(locator, tv, choice, CurDC);
return tv;
}
Type visitOtherConstructorDeclRefExpr(OtherConstructorDeclRefExpr *E) {
return E->getType();
}
Type visitSuperRefExpr(SuperRefExpr *E) {
if (E->getType())
return E->getType();
// Resolve the super type of 'self'.
auto *selfDecl = E->getSelf();
DeclContext *typeContext = selfDecl->getDeclContext()->getParent();
assert(typeContext);
auto selfTy =
CS.DC->mapTypeIntoContext(typeContext->getDeclaredInterfaceType());
auto superclassTy = selfTy->getSuperclass();
if (!superclassTy)
return Type();
if (selfDecl->getInterfaceType()->is<MetatypeType>())
superclassTy = MetatypeType::get(superclassTy);
return superclassTy;
}
Type
resolveTypeReferenceInExpression(TypeRepr *repr,
TypeResolutionOptions options,
const ConstraintLocatorBuilder &locator) {
// Introduce type variables for unbound generics.
const auto genericOpener = OpenUnboundGenericType(CS, locator);
const auto placeholderHandler = HandlePlaceholderType(CS, locator);
// Add a PackElementOf constraint for 'each T' type reprs.
PackExpansionExpr *elementEnv = nullptr;
if (!OuterExpansions.empty()) {
options |= TypeResolutionFlags::AllowPackReferences;
elementEnv = OuterExpansions.back();
}
const auto packElementOpener = OpenPackElementType(CS, locator, elementEnv);
const auto result = TypeResolution::resolveContextualType(
repr, CS.DC, options, genericOpener, placeholderHandler,
packElementOpener);
if (result->hasError()) {
CS.recordFix(
IgnoreInvalidASTNode::create(CS, CS.getConstraintLocator(locator)));
return CS.createTypeVariable(CS.getConstraintLocator(repr),
TVO_CanBindToHole);
}
// Diagnose top-level usages of placeholder types.
if (auto *ty = dyn_cast<PlaceholderTypeRepr>(repr->getWithoutParens())) {
auto *loc = CS.getConstraintLocator(locator, {LocatorPathElt::PlaceholderType(ty)});
CS.recordFix(IgnoreInvalidPlaceholder::create(CS, loc));
}
return result;
}
Type visitTypeExpr(TypeExpr *E) {
Type type;
// If this is an implicit TypeExpr, don't validate its contents.
auto *const locator = CS.getConstraintLocator(E);
if (E->isImplicit()) {
type = CS.getInstanceType(CS.cacheType(E));
assert(type && "Implicit type expr must have type set!");
type = CS.replaceInferableTypesWithTypeVars(type, locator);
} else {
auto *repr = E->getTypeRepr();
assert(repr && "Explicit node has no type repr!");
type = resolveTypeReferenceInExpression(
repr, TypeResolverContext::InExpression, locator);
}
if (!type || type->hasError()) return Type();
return MetatypeType::get(type);
}
Type visitTypeValueExpr(TypeValueExpr *E) {
auto ty = E->getParamDecl()->getValueType();
if (!ty || ty->hasError())
return recordInvalidNode(E);
return ty;
}
Type visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *expr) {
llvm_unreachable("Already type-checked");
}
Type visitOverloadedDeclRefExpr(OverloadedDeclRefExpr *expr) {
// For a reference to an overloaded declaration, we create a type variable
// that will be equal to different types depending on which overload
// is selected.
auto locator = CS.getConstraintLocator(expr);
auto tv = CS.createTypeVariable(locator,
TVO_CanBindToLValue | TVO_CanBindToNoEscape);
ArrayRef<ValueDecl*> decls = expr->getDecls();
SmallVector<OverloadChoice, 4> choices;
for (unsigned i = 0, n = decls.size(); i != n; ++i) {
// If the result is invalid, skip it.
// FIXME: Note this as invalid, in case we don't find a solution,
// so we don't let errors cascade further.
if (decls[i]->isInvalid())
continue;
OverloadChoice choice =
OverloadChoice(Type(), decls[i], expr->getFunctionRefInfo());
choices.push_back(choice);
}
if (choices.empty()) {
// There are no suitable overloads. Just fail.
return nullptr;
}
// Record this overload set.
CS.addOverloadSet(tv, choices, CurDC, locator);
return tv;
}
Type visitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *expr) {
// This is an error case, where we're trying to use type inference
// to help us determine which declaration the user meant to refer to.
// FIXME: Do we need to note that we're doing some kind of recovery?
return CS.createTypeVariable(CS.getConstraintLocator(expr),
TVO_CanBindToLValue |
TVO_CanBindToNoEscape);
}
Type visitMemberRefExpr(MemberRefExpr *expr) {
return addMemberRefConstraints(
expr, expr->getBase(), expr->getMember().getDecl(),
/*FIXME:*/ FunctionRefInfo::doubleBaseNameApply());
}
Type visitDynamicMemberRefExpr(DynamicMemberRefExpr *expr) {
llvm_unreachable("Already typechecked");
}
void setUnresolvedBaseType(UnresolvedMemberExpr *UME, Type ty) {
UnresolvedBaseTypes.insert({UME, ty});
}
Type getUnresolvedBaseType(UnresolvedMemberExpr *UME) {
auto result = UnresolvedBaseTypes.find(UME);
assert(result != UnresolvedBaseTypes.end());
return result->second;
}
virtual Type visitUnresolvedMemberExpr(UnresolvedMemberExpr *expr) {
auto baseLocator = CS.getConstraintLocator(
expr,
ConstraintLocator::MemberRefBase);
auto memberLocator
= CS.getConstraintLocator(expr, ConstraintLocator::UnresolvedMember);
// Since base type in this case is completely dependent on context it
// should be marked as a potential hole.
auto baseTy = CS.createTypeVariable(baseLocator, TVO_CanBindToNoEscape |
TVO_CanBindToHole);
setUnresolvedBaseType(expr, baseTy);
auto memberTy = CS.createTypeVariable(
memberLocator, TVO_CanBindToLValue | TVO_CanBindToNoEscape);
// An unresolved member expression '.member' is modeled as a value member
// constraint
//
// T0.Type[.member] == T1
//
// for fresh type variables T0 and T1, which pulls out a static
// member, i.e., an enum case or a static variable.
auto baseMetaTy = MetatypeType::get(baseTy);
CS.addUnresolvedValueMemberConstraint(baseMetaTy, expr->getName(),
memberTy, CurDC,
expr->getFunctionRefInfo(),
memberLocator);
return memberTy;
}
Type visitUnresolvedMemberChainResultExpr(
UnresolvedMemberChainResultExpr *expr) {
auto *tail = expr->getSubExpr();
auto memberTy = CS.getType(tail);
auto *base = expr->getChainBase();
assert(base == TypeChecker::getUnresolvedMemberChainBase(tail));
// The result type of the chain is represented by a new type variable.
auto locator = CS.getConstraintLocator(
expr, ConstraintLocator::UnresolvedMemberChainResult);
auto chainResultTy = CS.createTypeVariable(
locator,
TVO_CanBindToLValue | TVO_CanBindToHole | TVO_CanBindToNoEscape);
auto chainBaseTy = getUnresolvedBaseType(base);
// The result of the last element of the chain must be convertible to the
// whole chain, and the type of the whole chain must be equal to the base.
CS.addConstraint(ConstraintKind::Conversion, memberTy, chainResultTy,
locator);
CS.addConstraint(ConstraintKind::UnresolvedMemberChainBase, chainResultTy,
chainBaseTy, locator);
return chainResultTy;
}
Type visitUnresolvedDotExpr(UnresolvedDotExpr *expr) {
// UnresolvedDot applies the base to remove a single curry level from a
// member reference without using an applicable function constraint so
// we record the call argument matching here so it can be found later when
// a solution is applied to the AST.
CS.recordMatchCallArgumentResult(
CS.getConstraintLocator(expr, ConstraintLocator::ApplyArgument),
MatchCallArgumentResult::forArity(1));
// If this is Builtin.type_join*, just return any type and move
// on since we're going to discard this, and creating any type
// variables for the reference will cause problems.
auto &ctx = CS.getASTContext();
auto typeOperation = getTypeOperation(expr, ctx);
if (typeOperation != TypeOperation::None)
return ctx.getAnyExistentialType();
// If this is `Builtin.trigger_fallback_diagnostic()`, fail
// without producing any diagnostics, in order to test fallback error.
if (isTriggerFallbackDiagnosticBuiltin(expr, ctx))
return Type();
// Open a member constraint for constructor delegations on the
// subexpr type.
if (TypeChecker::getSelfForInitDelegationInConstructor(CS.DC, expr)) {
auto baseTy = CS.getType(expr->getBase())
->getWithoutSpecifierType();
// 'self' or 'super' will reference an instance, but the constructor
// is semantically a member of the metatype. This:
// self.init()
// super.init()
// is really more like:
// self = Self.init()
// self.super = Super.init()
baseTy = MetatypeType::get(baseTy, ctx);
auto memberTypeLoc = CS.getConstraintLocator(
expr, LocatorPathElt::ConstructorMemberType(
/*shortFormOrSelfDelegating*/ true));
auto methodTy =
CS.createTypeVariable(memberTypeLoc, TVO_CanBindToNoEscape);
// HACK: Bind the function's parameter list as a tuple to a type
// variable. This only exists to preserve compatibility with
// rdar://85263844, as it can affect the prioritization of bindings,
// which can affect behavior for tuple matching as tuple subtyping is
// currently a *weaker* constraint than tuple conversion.
if (!CS.getASTContext().isSwiftVersionAtLeast(6)) {
auto paramTypeVar = CS.createTypeVariable(
CS.getConstraintLocator(expr, ConstraintLocator::ApplyArgument),
TVO_CanBindToLValue | TVO_CanBindToInOut | TVO_CanBindToNoEscape |
TVO_CanBindToPack);
CS.addConstraint(ConstraintKind::BindTupleOfFunctionParams, methodTy,
paramTypeVar, CS.getConstraintLocator(expr));
}
CS.addValueMemberConstraint(
baseTy, expr->getName(), methodTy, CurDC,
expr->getFunctionRefInfo(),
/*outerAlternatives=*/{},
CS.getConstraintLocator(expr,
ConstraintLocator::ConstructorMember));
// The result of the expression is the partial application of the
// constructor to the subexpression.
return methodTy;
}
return addMemberRefConstraints(expr, expr->getBase(), expr->getName(),
expr->getFunctionRefInfo(),
expr->getOuterAlternatives());
}
/// Given a set of specialization arguments, resolve those arguments and
/// introduce them as an explicit generic arguments constraint.
///
/// \returns true if resolving any of the specialization types failed.
void addSpecializationConstraint(ConstraintLocator *locator, Type boundType,
SourceLoc lAngleLoc,
ArrayRef<TypeRepr *> specializationArgs) {
// Resolve each type.
SmallVector<Type, 2> specializationArgTypes;
auto options =
TypeResolutionOptions(TypeResolverContext::InExpression);
ConstraintLocatorBuilder locBuilder(locator);
for (auto idx : indices(specializationArgs)) {
auto specializationArg = specializationArgs[idx];
auto argLocator =
locBuilder.withPathElement(LocatorPathElt::GenericArgument(idx));
PackExpansionExpr *elementEnv = nullptr;
if (!OuterExpansions.empty()) {
options |= TypeResolutionFlags::AllowPackReferences;
elementEnv = OuterExpansions.back();
}
auto result = TypeResolution::resolveContextualType(
specializationArg, CurDC, options,
// Introduce type variables for unbound generics.
OpenUnboundGenericType(CS, argLocator),
HandlePlaceholderType(CS, argLocator),
OpenPackElementType(CS, argLocator, elementEnv));
if (result->hasError()) {
auto &ctxt = CS.getASTContext();
result = PlaceholderType::get(ctxt, specializationArg);
ctxt.Diags.diagnose(lAngleLoc,
diag::while_parsing_as_left_angle_bracket);
}
specializationArgTypes.push_back(result);
}
auto constraint = Constraint::create(
CS, ConstraintKind::ExplicitGenericArguments, boundType,
PackType::get(CS.getASTContext(), specializationArgTypes), locator);
CS.addUnsolvedConstraint(constraint);
CS.activateConstraint(constraint);
}
Type visitUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *expr) {
auto baseTy = CS.getType(expr->getSubExpr());
auto *overloadLocator = CS.getConstraintLocator(expr->getSubExpr());
addSpecializationConstraint(
overloadLocator, baseTy->getMetatypeInstanceType(),
expr->getLAngleLoc(), expr->getUnresolvedParams());
return baseTy;
}
Type visitSequenceExpr(SequenceExpr *expr) {
// If a SequenceExpr survived until CSGen, then there was an upstream
// error that was already reported.
return Type();
}
Type visitArrowExpr(ArrowExpr *expr) {
// If an ArrowExpr survived until CSGen, then there was an upstream
// error that was already reported.
return Type();
}
Type visitIdentityExpr(IdentityExpr *expr) {
return CS.getType(expr->getSubExpr());
}
Type visitCopyExpr(CopyExpr *expr) {
auto valueTy = CS.createTypeVariable(CS.getConstraintLocator(expr),
TVO_PrefersSubtypeBinding |
TVO_CanBindToNoEscape);
CS.addConstraint(ConstraintKind::Equal, valueTy,
CS.getType(expr->getSubExpr()),
CS.getConstraintLocator(expr));
return valueTy;
}
Type visitConsumeExpr(ConsumeExpr *expr) {
auto valueTy = CS.createTypeVariable(CS.getConstraintLocator(expr),
TVO_PrefersSubtypeBinding |
TVO_CanBindToNoEscape);
CS.addConstraint(ConstraintKind::Equal, valueTy,
CS.getType(expr->getSubExpr()),
CS.getConstraintLocator(expr));
return valueTy;
}
Type visitAnyTryExpr(AnyTryExpr *expr) {
return CS.getType(expr->getSubExpr());
}
Type visitOptionalTryExpr(OptionalTryExpr *expr) {
auto valueTy = CS.createTypeVariable(CS.getConstraintLocator(expr),
TVO_PrefersSubtypeBinding |
TVO_CanBindToNoEscape);
Type optTy = getOptionalType(expr->getSubExpr()->getLoc(), valueTy);
if (!optTy)
return Type();
bool isDelegationToOptionalInit = false;
{
Expr *e = expr->getSubExpr();
while (true) {
e = e->getSemanticsProvidingExpr();
// Look through force-value expressions.
if (auto *FVE = dyn_cast<ForceValueExpr>(e)) {
e = FVE->getSubExpr();
continue;
}
break;
}
if (auto *CE = dyn_cast<CallExpr>(e)) {
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(CE->getFn())) {
if (!CS.getType(UDE->getBase())->is<AnyMetatypeType>()) {
auto overload =
CS.findSelectedOverloadFor(CS.getConstraintLocator(
UDE, ConstraintLocator::ConstructorMember));
if (overload) {
auto *decl = overload->choice.getDeclOrNull();
if (decl && isa<ConstructorDecl>(decl) &&
decl->getDeclContext()
->getSelfNominalTypeDecl()
->isOptionalDecl())
isDelegationToOptionalInit = true;
}
}
}
}
}
// Prior to Swift 5, 'try?' always adds an additional layer of
// optionality, even if the sub-expression was already optional.
//
// NB Keep adding the additional layer in Swift 5 and on if this 'try?'
// applies to a delegation to an 'Optional' initializer, or else we won't
// discern the difference between a failure and a constructed value.
if (CS.getASTContext().LangOpts.isSwiftVersionAtLeast(5) &&
!isDelegationToOptionalInit) {
CS.addConstraint(ConstraintKind::Conversion,
CS.getType(expr->getSubExpr()), optTy,
CS.getConstraintLocator(expr));
} else {
CS.addConstraint(ConstraintKind::OptionalObject,
optTy, CS.getType(expr->getSubExpr()),
CS.getConstraintLocator(expr));
}
return optTy;
}
virtual Type visitParenExpr(ParenExpr *expr) {
// If the ParenExpr contains a pack expansion, generate a tuple
// type containing the pack expansion type.
if (CS.getType(expr->getSubExpr())->getAs<PackExpansionType>()) {
return TupleType::get({CS.getType(expr->getSubExpr())},
CS.getASTContext());
}
if (auto favoredTy = CS.getFavoredType(expr->getSubExpr())) {
CS.setFavoredType(expr, favoredTy);
}
return CS.getType(expr->getSubExpr());
}
Type visitTupleExpr(TupleExpr *expr) {
// The type of a tuple expression is simply a tuple of the types of
// its subexpressions.
SmallVector<TupleTypeElt, 4> elements;
elements.reserve(expr->getNumElements());
for (unsigned i = 0, n = expr->getNumElements(); i != n; ++i) {
elements.emplace_back(CS.getType(expr->getElement(i)),
expr->getElementName(i));
}
return TupleType::get(elements, CS.getASTContext());
}
Type visitSubscriptExpr(SubscriptExpr *expr) {
ValueDecl *decl = nullptr;
if (expr->hasDecl()) {
decl = expr->getDecl().getDecl();
if (decl->isInvalid())
return Type();
}
auto *base = expr->getBase();
if (!isValidBaseOfMemberRef(base, diag::cannot_subscript_nil_literal))
return nullptr;
return addSubscriptConstraints(expr, CS.getType(base), decl,
expr->getArgs());
}
Type visitArrayExpr(ArrayExpr *expr) {
auto &ctx = CS.getASTContext();
// An array expression can be of a type T that conforms to the
// ExpressibleByArrayLiteral protocol.
auto *arrayProto = TypeChecker::getProtocol(
ctx, expr->getLoc(),
KnownProtocolKind::ExpressibleByArrayLiteral);
if (!arrayProto) {
return Type();
}
// Assume that ExpressibleByArrayLiteral contains a single associated type.
auto *elementAssocTy = arrayProto->getAssociatedTypeMembers()[0];
if (!elementAssocTy)
return Type();
auto locator = CS.getConstraintLocator(expr);
auto contextualType = CS.getContextualType(expr, /*forConstraint=*/false);
auto contextualPurpose = CS.getContextualTypePurpose(expr);
auto joinElementTypes = [&](std::optional<Type> elementType) {
const auto elements = expr->getElements();
unsigned index = 0;
using Iterator = decltype(elements)::iterator;
CS.addJoinConstraint<Iterator>(
locator, elements.begin(), elements.end(), elementType,
[&](const auto it) {
auto *locator = CS.getConstraintLocator(
expr, LocatorPathElt::TupleElement(index++));
return std::make_pair(CS.getType(*it), locator);
});
};
// If a contextual type exists for this expression, apply it directly.
if (contextualType && contextualType->isArrayType()) {
// Now that we know we're actually going to use the type, get the
// version for use in a constraint.
contextualType = CS.getContextualType(expr, /*forConstraint=*/true);
// FIXME: This is the wrong place to be opening the opaque type.
contextualType = CS.openOpaqueType(
contextualType, contextualPurpose, locator, /*ownerDecl=*/nullptr);
Type arrayElementType = contextualType->isArrayType();
CS.addConstraint(ConstraintKind::LiteralConformsTo, contextualType,
arrayProto->getDeclaredInterfaceType(),
locator);
joinElementTypes(arrayElementType);
return contextualType;
}
// Produce a specialized diagnostic if this is an attempt to initialize
// or convert an array literal to a dictionary e.g.
// `let _: [String: Int] = ["A", 0]`
auto isDictionaryContextualType = [&](Type contextualType) -> bool {
if (!contextualType)
return false;
auto type = contextualType->lookThroughAllOptionalTypes();
if (lookupConformance(type, arrayProto))
return false;
if (auto *proto = ctx.getProtocol(KnownProtocolKind::ExpressibleByDictionaryLiteral))
if (lookupConformance(type, proto))
return true;
return false;
};
if (isDictionaryContextualType(contextualType)) {
auto &DE = CS.getASTContext().Diags;
auto numElements = expr->getNumElements();
// Empty and single element array literals with dictionary contextual
// types are fixed during solving, so continue as normal in those
// cases.
if (numElements > 1) {
bool isIniting =
CS.getContextualTypePurpose(expr) == CTP_Initialization;
DE.diagnose(expr->getStartLoc(), diag::should_use_dictionary_literal,
contextualType->lookThroughAllOptionalTypes(), isIniting);
auto diagnostic =
DE.diagnose(expr->getStartLoc(), diag::meant_dictionary_lit);
// If there is an even number of elements in the array, let's produce
// a fix-it which suggests to replace "," with ":" to form a dictionary
// literal.
if ((numElements & 1) == 0) {
const auto commaLocs = expr->getCommaLocs();
if (commaLocs.size() == numElements - 1) {
for (unsigned i = 0, e = numElements / 2; i != e; ++i)
diagnostic.fixItReplace(commaLocs[i * 2], ":");
}
}
return nullptr;
}
}
auto arrayTy = CS.createTypeVariable(locator,
TVO_PrefersSubtypeBinding |
TVO_CanBindToNoEscape);
// The array must be an array literal type.
CS.addConstraint(ConstraintKind::LiteralConformsTo, arrayTy,
arrayProto->getDeclaredInterfaceType(),
locator);
// Its subexpression should be convertible to a tuple (T.Element...).
Type arrayElementTy = DependentMemberType::get(arrayTy, elementAssocTy);
// Introduce conversions from each element to the element type of the
// array.
joinElementTypes(arrayElementTy);
// The array element type defaults to 'Any'.
CS.addConstraint(ConstraintKind::Defaultable, arrayElementTy,
ctx.getAnyExistentialType(), locator);
return arrayTy;
}
static bool isMergeableValueKind(Expr *expr) {
return isa<StringLiteralExpr>(expr) || isa<IntegerLiteralExpr>(expr) ||
isa<FloatLiteralExpr>(expr);
}
Type visitDictionaryExpr(DictionaryExpr *expr) {
ASTContext &C = CS.getASTContext();
// A dictionary expression can be of a type T that conforms to the
// ExpressibleByDictionaryLiteral protocol.
// FIXME: This isn't actually used for anything at the moment.
ProtocolDecl *dictionaryProto = TypeChecker::getProtocol(
C, expr->getLoc(), KnownProtocolKind::ExpressibleByDictionaryLiteral);
if (!dictionaryProto) {
return Type();
}
// FIXME: Protect against broken standard library.
auto keyAssocTy = dictionaryProto->getAssociatedType(C.Id_Key);
auto valueAssocTy = dictionaryProto->getAssociatedType(C.Id_Value);
auto locator = CS.getConstraintLocator(expr);
auto contextualType = CS.getContextualType(expr, /*forConstraint=*/false);
auto contextualPurpose = CS.getContextualTypePurpose(expr);
// If a contextual type exists for this expression and is a dictionary
// type, apply it directly.
if (contextualType && ConstraintSystem::isDictionaryType(contextualType)) {
// Now that we know we're actually going to use the type, get the
// version for use in a constraint.
contextualType = CS.getContextualType(expr, /*forConstraint=*/true);
// FIXME: This is the wrong place to be opening the opaque type.
auto openedType =
CS.openOpaqueType(contextualType, contextualPurpose, locator,
/*ownerDecl=*/nullptr);
auto dictionaryKeyValue =
ConstraintSystem::isDictionaryType(openedType);
Type contextualDictionaryKeyType;
Type contextualDictionaryValueType;
std::tie(contextualDictionaryKeyType,
contextualDictionaryValueType) = *dictionaryKeyValue;
// Form an explicit tuple type from the contextual type's key and value types.
TupleTypeElt tupleElts[2] = { TupleTypeElt(contextualDictionaryKeyType),
TupleTypeElt(contextualDictionaryValueType) };
Type contextualDictionaryElementType = TupleType::get(tupleElts, C);
CS.addConstraint(ConstraintKind::LiteralConformsTo, openedType,
dictionaryProto->getDeclaredInterfaceType(), locator);
unsigned index = 0;
for (auto element : expr->getElements()) {
CS.addConstraint(ConstraintKind::Conversion,
CS.getType(element),
contextualDictionaryElementType,
CS.getConstraintLocator(
expr, LocatorPathElt::TupleElement(index++)));
}
return openedType;
}
auto dictionaryTy = CS.createTypeVariable(locator,
TVO_PrefersSubtypeBinding |
TVO_CanBindToNoEscape);
// The dictionary must be a dictionary literal type.
CS.addConstraint(ConstraintKind::LiteralConformsTo, dictionaryTy,
dictionaryProto->getDeclaredInterfaceType(),
locator);
// Its subexpression should be convertible to a tuple ((T.Key,T.Value)...).
ConstraintLocatorBuilder locatorBuilder(locator);
auto dictionaryKeyTy = DependentMemberType::get(dictionaryTy,
keyAssocTy);
auto dictionaryValueTy = DependentMemberType::get(dictionaryTy,
valueAssocTy);
TupleTypeElt tupleElts[2] = { TupleTypeElt(dictionaryKeyTy),
TupleTypeElt(dictionaryValueTy) };
Type elementTy = TupleType::get(tupleElts, C);
// Keep track of which elements have been "merged". This way, we won't create
// needless conversion constraints for elements whose equivalence classes have
// been merged.
llvm::DenseSet<Expr *> mergedElements;
// If no contextual type is present, Merge equivalence classes of key
// and value types as necessary.
if (!CS.getContextualType(expr, /*forConstraint=*/false)) {
for (auto element1 : expr->getElements()) {
for (auto element2 : expr->getElements()) {
if (element1 == element2)
continue;
auto tty1 = CS.getType(element1)->getAs<TupleType>();
auto tty2 = CS.getType(element2)->getAs<TupleType>();
if (tty1 && tty2) {
auto mergedKey = false;
auto mergedValue = false;
auto keyTyvar1 = tty1->getElementTypes()[0]->
getAs<TypeVariableType>();
auto keyTyvar2 = tty2->getElementTypes()[0]->
getAs<TypeVariableType>();
auto keyExpr1 = cast<TupleExpr>(element1)->getElements()[0];
auto keyExpr2 = cast<TupleExpr>(element2)->getElements()[0];
if (keyExpr1->getKind() == keyExpr2->getKind() &&
isMergeableValueKind(keyExpr1)) {
mergedKey = mergeRepresentativeEquivalenceClasses(CS,
keyTyvar1, keyTyvar2);
}
auto valueTyvar1 = tty1->getElementTypes()[1]->
getAs<TypeVariableType>();
auto valueTyvar2 = tty2->getElementTypes()[1]->
getAs<TypeVariableType>();
auto elemExpr1 = cast<TupleExpr>(element1)->getElements()[1];
auto elemExpr2 = cast<TupleExpr>(element2)->getElements()[1];
if (elemExpr1->getKind() == elemExpr2->getKind() &&
isMergeableValueKind(elemExpr1)) {
mergedValue = mergeRepresentativeEquivalenceClasses(CS,
valueTyvar1, valueTyvar2);
}
if (mergedKey && mergedValue)
mergedElements.insert(element2);
}
}
}
}
// Introduce conversions from each element to the element type of the
// dictionary. (If the equivalence class of an element has already been
// merged with a previous one, skip it.)
unsigned index = 0;
for (auto element : expr->getElements()) {
if (!mergedElements.count(element))
CS.addConstraint(ConstraintKind::Conversion,
CS.getType(element),
elementTy,
CS.getConstraintLocator(
expr, LocatorPathElt::TupleElement(index++)));
}
// The dictionary key type defaults to 'AnyHashable'.
auto &ctx = CS.getASTContext();
if (dictionaryKeyTy->isTypeVariableOrMember() &&
ctx.getAnyHashableDecl()) {
auto anyHashable = ctx.getAnyHashableDecl();
CS.addConstraint(ConstraintKind::Defaultable, dictionaryKeyTy,
anyHashable->getDeclaredInterfaceType(), locator);
}
// The dictionary value type defaults to 'Any'.
if (dictionaryValueTy->isTypeVariableOrMember()) {
CS.addConstraint(ConstraintKind::Defaultable, dictionaryValueTy,
ctx.getAnyExistentialType(), locator);
}
return dictionaryTy;
}
Type visitDynamicSubscriptExpr(DynamicSubscriptExpr *expr) {
return addSubscriptConstraints(expr, CS.getType(expr->getBase()),
/*decl*/ nullptr, expr->getArgs());
}
Type visitTupleElementExpr(TupleElementExpr *expr) {
ASTContext &context = CS.getASTContext();
DeclNameRef name(
context.getIdentifier(llvm::utostr(expr->getFieldNumber())));
return addMemberRefConstraints(expr, expr->getBase(), name,
FunctionRefInfo::unappliedBaseName(),
/*outerAlternatives=*/{});
}
/// If \p allowResultBindToHole is \c true, we always allow the closure's
/// result type to bind to a hole, otherwise the result type may only bind
/// to a hole if the closure does not participate in type inference. Setting
/// \p allowResultBindToHole to \c true is useful when ignoring a closure
/// argument in a function call after the code completion token and thus
/// wanting to ignore the closure's type.
FunctionType *inferClosureType(ClosureExpr *closure,
bool allowResultBindToHole = false) {
SmallVector<AnyFunctionType::Param, 4> closureParams;
if (auto *paramList = closure->getParameters()) {
for (unsigned i = 0, n = paramList->size(); i != n; ++i) {
auto *param = paramList->get(i);
auto *paramLoc =
CS.getConstraintLocator(closure, LocatorPathElt::TupleElement(i));
// If one of the parameters represents a destructured tuple
// e.g. `{ (x: Int, (y: Int, z: Int)) in ... }` let's fail
// inference here and not attempt to solve the system because:
//
// a. Destructuring has already been diagnosed by the parser;
// b. Body of the closure would have error expressions for
// each incorrect parameter reference and solver wouldn't
// be able to produce any viable solutions.
if (param->isDestructured())
return nullptr;
Type externalType;
if (param->getTypeRepr()) {
auto declaredTy = param->getTypeInContext();
// If closure parameter couldn't be resolved, let's record
// a fix to make sure that type resolution diagnosed the
// problem and replace it with a placeholder, so that solver
// can make forward progress (especially important for result
// builders).
if (declaredTy->hasError()) {
CS.recordFix(AllowRefToInvalidDecl::create(
CS, CS.getConstraintLocator(param)));
declaredTy = PlaceholderType::get(CS.getASTContext(), param);
}
externalType = CS.replaceInferableTypesWithTypeVars(declaredTy,
paramLoc);
} else {
// Let's allow parameters which haven't been explicitly typed
// to become holes by default, this helps in situations like
// `foo { a in }` where `foo` doesn't exist.
externalType = CS.createTypeVariable(
paramLoc,
TVO_CanBindToInOut | TVO_CanBindToNoEscape | TVO_CanBindToHole);
}
closureParams.push_back(param->toFunctionParam(externalType));
}
checkVariadicParameters(paramList, closure);
}
auto extInfo = CS.closureEffects(closure);
auto resultLocator =
CS.getConstraintLocator(closure, ConstraintLocator::ClosureResult);
auto thrownErrorLocator =
CS.getConstraintLocator(closure, ConstraintLocator::ClosureThrownError);
// Determine the thrown error type, when appropriate.
Type thrownErrorTy = [&] {
// Explicitly-specified thrown type.
if (closure->getExplicitThrownTypeRepr()) {
if (Type explicitType = closure->getExplicitThrownType())
return explicitType;
}
// Explicitly-specified 'throws' without a type is untyped throws.
// Use a NULL return here so that the inferred type is written with
// `throws` instead of `throws(any Error)`, although the two are
// semantically equivalent.
if (closure->getThrowsLoc().isValid())
return Type();
// Thrown type inferred from context.
if (auto contextualType = CS.getContextualType(
closure, /*forConstraint=*/false)) {
if (auto fnType = contextualType->getAs<AnyFunctionType>()) {
if (Type thrownErrorTy = fnType->getThrownError())
return thrownErrorTy;
}
}
// We do not try to infer a thrown error type if one isn't immediately
// available.
return Type();
}();
if (thrownErrorTy) {
// Record the thrown error type in the extended info for the function
// type of the closure.
extInfo = extInfo.withThrows(true, thrownErrorTy);
// Ensure that the thrown error type conforms to Error.
if (auto errorProto =
CS.getASTContext().getProtocol(KnownProtocolKind::Error)) {
CS.addConstraint(
ConstraintKind::ConformsTo, thrownErrorTy,
errorProto->getDeclaredInterfaceType(), thrownErrorLocator);
}
}
// Closure expressions always have function type. In cases where a
// parameter or return type is omitted, a fresh type variable is used to
// stand in for that parameter or return type, allowing it to be inferred
// from context.
Type resultTy = [&] {
if (closure->hasExplicitResultType()) {
if (auto declaredTy = closure->getExplicitResultType()) {
return declaredTy;
}
auto options =
TypeResolutionOptions(TypeResolverContext::InExpression);
options.setContext(TypeResolverContext::ClosureExpr);
const auto resolvedTy = resolveTypeReferenceInExpression(
closure->getExplicitResultTypeRepr(), options, resultLocator);
if (resolvedTy)
return resolvedTy;
}
// Because we are only pulling out the result type from the contextual
// type, we avoid prematurely converting any inferrable types by setting
// forConstraint=false. Later on in inferClosureType we call
// replaceInferableTypesWithTypeVars before returning to ensure we don't
// introduce any placeholders into the constraint system.
if (auto contextualType =
CS.getContextualType(closure, /*forConstraint=*/false)) {
if (auto fnType = contextualType->getAs<FunctionType>())
return fnType->getResult();
}
// If no return type was specified, create a fresh type
// variable for it and mark it as possible hole.
//
// If this is a multi-statement closure, let's mark result
// as potential hole right away.
return Type(CS.createTypeVariable(
resultLocator, allowResultBindToHole ? TVO_CanBindToHole : 0));
}();
// Determine the isolation of the closure.
auto isolation = [&] {
// Priority goes to an explicit isolated parameter.
if (hasIsolatedParameter(closureParams))
return FunctionTypeIsolation::forParameter();
// Honor an explicit global actor. This is suppressed if the
// closure is async (but should it be?).
if (!extInfo.isAsync()) {
if (auto actorType = getExplicitGlobalActor(closure))
return FunctionTypeIsolation::forGlobalActor(actorType);
}
if (closure->getAttrs().hasAttribute<ConcurrentAttr>()) {
return FunctionTypeIsolation::forNonIsolated();
}
return FunctionTypeIsolation::forNonIsolated();
}();
extInfo = extInfo.withIsolation(isolation);
if (isolation.isGlobalActor() &&
CS.getASTContext().LangOpts.hasFeature(Feature::GlobalActorIsolatedTypesUsability)) {
extInfo = extInfo.withSendable();
}
auto *fnTy = FunctionType::get(closureParams, resultTy, extInfo);
return CS.replaceInferableTypesWithTypeVars(
fnTy, CS.getConstraintLocator(closure))->castTo<FunctionType>();
}
/// Produces a type for the given pattern, filling in any missing
/// type information with fresh type variables.
///
/// \param pattern The pattern.
///
/// \param locator The locator to use for generated constraints and
/// type variables.
///
/// \param bindPatternVarsOneWay When true, generate fresh type variables
/// for the types of each variable declared within the pattern, along
/// with a one-way constraint binding that to the type to which the
/// variable will be ascribed or inferred.
Type getTypeForPattern(
Pattern *pattern, ConstraintLocatorBuilder locator,
bool bindPatternVarsOneWay,
PatternBindingDecl *patternBinding = nullptr,
unsigned patternBindingIndex = 0) {
assert(pattern);
// Local function that must be called for each "return" throughout this
// function, to set the type of the pattern.
auto setType = [&](Type type) {
CS.setType(pattern, type);
return type;
};
switch (pattern->getKind()) {
case PatternKind::Paren: {
auto *paren = cast<ParenPattern>(pattern);
auto *subPattern = paren->getSubPattern();
auto underlyingType = getTypeForPattern(
subPattern,
locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)),
bindPatternVarsOneWay);
return setType(underlyingType);
}
case PatternKind::Binding: {
auto *subPattern = cast<BindingPattern>(pattern)->getSubPattern();
auto type = getTypeForPattern(subPattern, locator,
bindPatternVarsOneWay);
// Var doesn't affect the type.
return setType(type);
}
case PatternKind::Any: {
Type type;
// If this is a situation like `[let] _ = <expr>`, return
// initializer expression.
auto getInitializerExpr = [&locator]() -> Expr * {
auto last = locator.last();
if (!last)
return nullptr;
auto contextualTy = last->getAs<LocatorPathElt::ContextualType>();
return (contextualTy && contextualTy->isFor(CTP_Initialization))
? locator.trySimplifyToExpr()
: nullptr;
};
auto matchLoc =
locator.withPathElement(LocatorPathElt::PatternMatch(pattern));
// Always prefer a contextual type when it's available.
if (auto *initializer = getInitializerExpr()) {
// For initialization always assume a type of initializer.
type = CS.getType(initializer)->getRValueType();
} else {
type = CS.createTypeVariable(
CS.getConstraintLocator(matchLoc,
LocatorPathElt::AnyPatternDecl()),
TVO_CanBindToNoEscape | TVO_CanBindToHole);
}
return setType(type);
}
case PatternKind::Named: {
auto var = cast<NamedPattern>(pattern)->getDecl();
Type varType;
// Determine whether optionality will be required.
auto ROK = ReferenceOwnership::Strong;
if (auto *OA = var->getAttrs().getAttribute<ReferenceOwnershipAttr>())
ROK = OA->get();
auto optionality = optionalityOf(ROK);
// If we have a type from an initializer expression, and that
// expression does not produce an InOut type, use it. This
// will avoid exponential typecheck behavior in the case of
// tuples, nested arrays, and dictionary literals.
//
// FIXME: This should be handled in the solver, not here.
//
// Otherwise, create a new type variable.
if (var->getParentPatternBinding() &&
!var->hasAttachedPropertyWrapper() &&
optionality != ReferenceOwnershipOptionality::Required) {
if (auto boundExpr = locator.trySimplifyToExpr()) {
if (!boundExpr->isSemanticallyInOutExpr()) {
varType = CS.getType(boundExpr)->getRValueType();
}
}
}
auto matchLoc =
locator.withPathElement(LocatorPathElt::PatternMatch(pattern));
if (!varType) {
varType = CS.createTypeVariable(
CS.getConstraintLocator(matchLoc,
LocatorPathElt::NamedPatternDecl()),
TVO_CanBindToNoEscape | TVO_CanBindToHole);
// If this is either a `weak` declaration or capture e.g.
// `weak var ...` or `[weak self]`. Let's wrap type variable
// into an optional.
if (optionality == ReferenceOwnershipOptionality::Required)
varType = TypeChecker::getOptionalType(var->getLoc(), varType);
}
auto makeTypeLocatableIfPossible = [&var](Type type) -> Type {
if (auto loc = var->getLoc()) {
return LocatableType::get(loc, type);
}
return type;
};
auto useLocatableTypes = [&]() -> bool {
if (!CS.inSalvageMode())
return false;
return var->isImplicit() &&
var->getNameStr().starts_with("$__builder");
};
// When we are supposed to bind pattern variables, create a fresh
// type variable and a one-way constraint to assign it to either the
// deduced type or the externally-imposed type.
Type oneWayVarType;
if (bindPatternVarsOneWay) {
oneWayVarType = CS.createTypeVariable(
CS.getConstraintLocator(locator), TVO_CanBindToNoEscape);
// If there is externally-imposed type, and the variable
// is marked as `weak`, let's fallthrough and allow the
// `one-way` constraint to be fixed in diagnostic mode.
//
// That would make sure that type of this variable is
// recorded in the constraint system, which would then
// be used instead of `getVarType` upon discovering a
// reference to this variable in subsequent expression(s).
//
// If we let constraint generation fail here, it would trigger
// interface type request via `var->getType()` that would
// attempt to validate `weak` attribute, and produce a
// diagnostic in the middle of the solver path.
CS.addConstraint(ConstraintKind::OneWayEqual, oneWayVarType,
varType, locator);
if (useLocatableTypes())
oneWayVarType = makeTypeLocatableIfPossible(oneWayVarType);
}
// Ascribe a type to the declaration so it's always available to
// constraint system.
if (oneWayVarType) {
CS.setType(var, oneWayVarType);
} else {
// Otherwise, let's use the type of the pattern. The type
// of the declaration has to be r-value, so let's add an
// equality constraint if pattern type has any type variables
// that are allowed to be l-value.
bool foundLValueVars = false;
// Note that it wouldn't be always correct to allocate a single type
// variable, that disallows l-value types, to use as a declaration
// type because equality constraint would drop TVO_CanBindToLValue
// from the right-hand side (which is not the case for `OneWayEqual`)
// e.g.:
//
// struct S { var x, y: Int }
//
// func test(s: S) {
// let (x, y) = (s.x, s.y)
// }
//
// Single type variable approach results in the following constraint:
// `$T_x_y = ($T_s_x, $T_s_y)` where both `$T_s_x` and `$T_s_y` have
// to allow l-value, but `$T_x_y` does not. Early simplification of `=`
// constraint (due to right-hand side being a "concrete" tuple type)
// would drop l-value option from `$T_s_x` and `$T_s_y` which leads to
// a failure during member lookup because `x` and `y` are both
// `@lvalue Int`. To avoid that, declaration type would mimic pattern
// type with all l-value options stripped, so the equality constraint
// becomes `($T_x, $_T_y) = ($T_s_x, $T_s_y)` which doesn't result in
// stripping of l-value flag from the right-hand side since
// simplification can only happen when either side is resolved.
auto declTy = varType.transformRec([&](Type type) -> std::optional<Type> {
if (auto *typeVar = type->getAs<TypeVariableType>()) {
if (typeVar->getImpl().canBindToLValue()) {
foundLValueVars = true;
// Drop l-value from the options but preserve the rest.
auto options = typeVar->getImpl().getRawOptions();
options &= ~TVO_CanBindToLValue;
return Type(CS.createTypeVariable(typeVar->getImpl().getLocator(),
options));
}
}
return std::nullopt;
});
// If pattern types allows l-value types, let's create an
// equality constraint between r-value only declaration type
// and l-value pattern type that would take care of looking
// through l-values when necessary.
if (foundLValueVars) {
CS.addConstraint(ConstraintKind::Equal, declTy, varType,
CS.getConstraintLocator(locator));
}
if (useLocatableTypes())
declTy = makeTypeLocatableIfPossible(declTy);
CS.setType(var, declTy);
}
return setType(varType);
}
case PatternKind::Typed: {
// FIXME: Need a better locator for a pattern as a base.
// Compute the type ascribed to the pattern.
auto contextualPattern = patternBinding
? ContextualPattern::forPatternBindingDecl(
patternBinding, patternBindingIndex)
: ContextualPattern::forRawPattern(pattern, CurDC);
Type type = TypeChecker::typeCheckPattern(contextualPattern);
// Look through reference storage types.
type = type->getReferenceStorageReferent();
Type replacedType = CS.replaceInferableTypesWithTypeVars(type, locator);
Type openedType =
CS.openOpaqueType(replacedType, CTP_Initialization, locator,
patternBinding);
assert(openedType);
auto *subPattern = cast<TypedPattern>(pattern)->getSubPattern();
// Determine the subpattern type. It will be convertible to the
// ascribed type.
Type subPatternType = getTypeForPattern(
subPattern,
locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)),
bindPatternVarsOneWay);
// NOTE: The order here is important! Pattern matching equality is
// not symmetric (we need to fix that either by using a different
// constraint, or actually making it symmetric).
CS.addConstraint(
ConstraintKind::Equal, openedType, subPatternType,
locator.withPathElement(LocatorPathElt::PatternMatch(pattern)));
// FIXME [OPAQUE SUPPORT]: the distinction between where we want opaque
// types in opened vs. un-unopened form is *very* tricky. The pattern
// ultimately needs the un-opened type and it gets this from the set
// type of the expression.
CS.setType(pattern, replacedType);
return openedType;
}
case PatternKind::Tuple: {
auto tuplePat = cast<TuplePattern>(pattern);
SmallVector<TupleTypeElt, 4> tupleTypeElts;
tupleTypeElts.reserve(tuplePat->getNumElements());
for (unsigned i = 0, e = tuplePat->getNumElements(); i != e; ++i) {
auto &tupleElt = tuplePat->getElement(i);
auto *eltPattern = tupleElt.getPattern();
Type eltTy = getTypeForPattern(
eltPattern,
locator.withPathElement(LocatorPathElt::PatternMatch(eltPattern)),
bindPatternVarsOneWay);
tupleTypeElts.push_back(TupleTypeElt(eltTy, tupleElt.getLabel()));
}
return setType(TupleType::get(tupleTypeElts, CS.getASTContext()));
}
case PatternKind::OptionalSome: {
auto *subPattern = cast<OptionalSomePattern>(pattern)->getSubPattern();
// The subpattern must have optional type.
Type subPatternType = getTypeForPattern(
subPattern,
locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)),
bindPatternVarsOneWay);
return setType(OptionalType::get(subPatternType));
}
case PatternKind::Is: {
auto isPattern = cast<IsPattern>(pattern);
const Type castType = resolveTypeReferenceInExpression(
isPattern->getCastTypeRepr(), TypeResolverContext::InExpression,
locator.withPathElement(LocatorPathElt::PatternMatch(pattern)));
// Allow `is` pattern to infer type from context which is then going
// to be propaged down to its sub-pattern via conversion. This enables
// correct handling of patterns like `_ as Foo` where `_` would
// get a type of `Foo` but `is` pattern enclosing it could still be
// inferred from enclosing context.
auto isType =
CS.createTypeVariable(CS.getConstraintLocator(pattern),
TVO_CanBindToNoEscape | TVO_CanBindToHole);
// Make sure we can cast from the subpattern type to the type we're
// checking; if it's impossible, fail.
CS.addConstraint(
ConstraintKind::CheckedCast, isType, castType,
locator.withPathElement(LocatorPathElt::PatternMatch(pattern)));
if (auto *subPattern = isPattern->getSubPattern()) {
auto subPatternType = getTypeForPattern(
subPattern,
locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)),
bindPatternVarsOneWay);
// NOTE: The order here is important! Pattern matching equality is
// not symmetric (we need to fix that either by using a different
// constraint, or actually making it symmetric).
CS.addConstraint(
ConstraintKind::Equal, castType, subPatternType,
locator.withPathElement(LocatorPathElt::PatternMatch(pattern)));
}
return setType(isType);
}
case PatternKind::Bool:
return setType(CS.getASTContext().getBoolType());
case PatternKind::EnumElement: {
auto enumPattern = cast<EnumElementPattern>(pattern);
// Create a type variable to represent the pattern.
Type patternType =
CS.createTypeVariable(CS.getConstraintLocator(locator),
TVO_CanBindToNoEscape);
// Form the member constraint for a reference to a member of this
// type.
Type baseType;
Type memberType = CS.createTypeVariable(
CS.getConstraintLocator(locator),
TVO_CanBindToLValue | TVO_CanBindToNoEscape);
// Tuple splat is still allowed for patterns (with a warning in Swift 5)
// so we need to set a non-compound reference to make sure that e.g.
// `case test(x: Int, y: Int)` gets the labels preserved when matched
// with `case let .test(tuple)`.
auto functionRefInfo = FunctionRefInfo::unappliedBaseName();
if (enumPattern->hasSubPattern())
functionRefInfo = functionRefInfo.addingApplicationLevel();
// If sub-pattern is a tuple we'd need to mark reference as compound,
// that would make sure that the labels are dropped in cases
// when `case` has a single tuple argument (tuple explosion) or multiple
// arguments (tuple-to-tuple conversion).
// FIXME: We ought to be preserving labels and matching in the solver.
if (dyn_cast_or_null<TuplePattern>(enumPattern->getSubPattern()))
functionRefInfo = FunctionRefInfo::singleCompoundNameApply();
auto patternLocator =
locator.withPathElement(LocatorPathElt::PatternMatch(pattern));
if (enumPattern->getParentType() || enumPattern->getParentTypeRepr()) {
// Resolve the parent type.
const auto parentType = [&] {
auto *const patternMatchLoc = CS.getConstraintLocator(
locator, {LocatorPathElt::PatternMatch(pattern),
ConstraintLocator::ParentType});
// FIXME: Sometimes the parent type is realized eagerly in
// ResolvePattern::visitUnresolvedDotExpr, so we have to open it
// ex post facto. Remove this once we learn how to resolve patterns
// while generating constraints to keep the opening of generic types
// contained within the type resolver.
if (const auto preresolvedTy = enumPattern->getParentType()) {
const auto openedTy =
CS.replaceInferableTypesWithTypeVars(preresolvedTy,
patternMatchLoc);
assert(openedTy);
return openedTy;
}
return resolveTypeReferenceInExpression(
enumPattern->getParentTypeRepr(),
TypeResolverContext::InExpression, patternMatchLoc);
}();
// Perform member lookup into the parent's metatype.
Type parentMetaType = MetatypeType::get(parentType);
CS.addValueMemberConstraint(parentMetaType, enumPattern->getName(),
memberType, CurDC, functionRefInfo, {},
patternLocator);
// Parent type needs to be convertible to the pattern type; this
// accounts for cases where the pattern type is existential.
CS.addConstraint(
ConstraintKind::Conversion, parentType, patternType,
patternLocator.withPathElement(
ConstraintLocator::EnumPatternImplicitCastMatch));
baseType = parentType;
} else {
// Use the pattern type for member lookup.
CS.addUnresolvedValueMemberConstraint(
MetatypeType::get(patternType), enumPattern->getName(),
memberType, CurDC, functionRefInfo, patternLocator);
baseType = patternType;
}
if (auto subPattern = enumPattern->getSubPattern()) {
// When there is a subpattern, the member will have function type,
// and we're matching the type of that subpattern to the parameter
// types.
Type subPatternType = getTypeForPattern(
subPattern,
locator.withPathElement(LocatorPathElt::PatternMatch(subPattern)),
bindPatternVarsOneWay);
SmallVector<AnyFunctionType::Param, 4> params;
decomposeTuple(subPatternType, params);
// Remove parameter labels; they aren't used when matching cases,
// but outright conflicts will be checked during coercion.
for (auto &param : params) {
param = param.getWithoutLabels();
}
Type outputType = CS.createTypeVariable(
CS.getConstraintLocator(locator),
TVO_CanBindToNoEscape);
// Equal constraints require ExtInfo comparison.
// FIXME: Verify ExtInfo state is correct, not working by accident.
FunctionType::ExtInfo info;
Type functionType = FunctionType::get(params, outputType, info);
// TODO: Convert to own constraint? Note that ApplicableFn isn't quite
// right, as pattern matching has data flowing *into* the apply result
// and call arguments, not the other way around.
// NOTE: The order here is important! Pattern matching equality is
// not symmetric (we need to fix that either by using a different
// constraint, or actually making it symmetric).
CS.addConstraint(ConstraintKind::Equal, functionType, memberType,
patternLocator);
CS.addConstraint(ConstraintKind::Conversion, outputType, baseType,
patternLocator);
}
return setType(patternType);
}
case PatternKind::Expr: {
// We generate constraints for ExprPatterns in a separate pass. For
// now, just create a type variable.
return setType(CS.createTypeVariable(CS.getConstraintLocator(locator),
TVO_CanBindToNoEscape));
}
}
llvm_unreachable("Unhandled pattern kind");
}
Type visitCaptureListExpr(CaptureListExpr *expr) {
// The type of the capture list is just the type of its closure.
return CS.getType(expr->getClosureBody());
}
Type visitClosureExpr(ClosureExpr *closure) {
auto *locator = CS.getConstraintLocator(closure);
auto closureType = CS.createTypeVariable(locator, TVO_CanBindToNoEscape);
TypeVarRefCollector refCollector(CS, /*DC*/ closure, locator);
// Walk the capture list if this closure has one, because it could
// reference declarations from the outer closure.
if (auto *captureList =
getAsExpr<CaptureListExpr>(CS.getParentExpr(closure))) {
captureList->walk(refCollector);
} else {
closure->walk(refCollector);
}
auto inferredType = inferClosureType(closure);
if (!inferredType || inferredType->hasError())
return Type();
auto referencedVars = refCollector.getTypeVars();
CS.addUnsolvedConstraint(
Constraint::create(CS, ConstraintKind::FallbackType, closureType,
inferredType, locator, referencedVars));
if (!OuterExpansions.empty())
CS.setCapturedExpansions(closure, OuterExpansions);
CS.setClosureType(closure, inferredType);
return closureType;
}
Type visitAutoClosureExpr(AutoClosureExpr *expr) {
// AutoClosureExpr is introduced by CSApply.
llvm_unreachable("Already type-checked");
}
Type visitInOutExpr(InOutExpr *expr) {
// The address-of operator produces an explicit inout T from an lvalue T.
// We model this with the constraint
//
// S < lvalue T
//
// where T is a fresh type variable.
auto lvalue = CS.createTypeVariable(CS.getConstraintLocator(expr),
TVO_CanBindToNoEscape);
auto bound = LValueType::get(lvalue);
auto result = InOutType::get(lvalue);
CS.addConstraint(ConstraintKind::Conversion,
CS.getType(expr->getSubExpr()), bound,
CS.getConstraintLocator(expr));
return result;
}
Type visitVarargExpansionExpr(VarargExpansionExpr *expr) {
// Create a fresh type variable.
auto element = CS.createTypeVariable(CS.getConstraintLocator(expr),
TVO_CanBindToNoEscape);
// Try to build the appropriate type for a variadic argument list of
// the fresh element type. If that failed, just bail out.
auto variadicSeq = VariadicSequenceType::get(element);
// Require the operand to be convertible to the array type.
CS.addConstraint(ConstraintKind::Conversion,
CS.getType(expr->getSubExpr()), variadicSeq,
CS.getConstraintLocator(expr));
return variadicSeq;
}
void collectExpandedPacks(PackExpansionExpr *expansion,
SmallVectorImpl<ASTNode> &packs) {
struct PackCollector : public ASTWalker {
private:
ConstraintSystem &CS;
SmallVectorImpl<ASTNode> &Packs;
public:
PackCollector(ConstraintSystem &cs, SmallVectorImpl<ASTNode> &packs)
: CS(cs), Packs(packs) {}
/// Walk everything that's available.
MacroWalking getMacroWalkingBehavior() const override {
return MacroWalking::ArgumentsAndExpansion;
}
virtual PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
// Don't walk into nested pack expansions
if (isa<PackExpansionExpr>(E)) {
return Action::SkipNode(E);
}
if (isa<PackElementExpr>(E)) {
Packs.push_back(E);
}
if (auto *declRef = dyn_cast<DeclRefExpr>(E)) {
auto type = CS.getTypeIfAvailable(declRef);
if (!type)
return Action::Continue(E);
if (type->is<ElementArchetypeType>() &&
CS.hasFixFor(CS.getConstraintLocator(declRef),
FixKind::IgnoreMissingEachKeyword)) {
auto *packElementExpr =
PackElementExpr::create(CS.getASTContext(),
/*eachLoc=*/{}, declRef,
/*implicit=*/true, type);
Packs.push_back(CS.cacheType(packElementExpr));
}
}
return Action::Continue(E);
}
virtual PreWalkAction walkToTypeReprPre(TypeRepr *T) override {
// Don't walk into nested pack expansions
if (isa<PackExpansionTypeRepr>(T)) {
return Action::SkipNode();
}
if (isa<PackElementTypeRepr>(T)) {
Packs.push_back(T);
}
return Action::Continue();
}
} packCollector(CS, packs);
expansion->getPatternExpr()->walk(packCollector);
}
Type visitPackExpansionExpr(PackExpansionExpr *expr) {
assert(OuterExpansions.back() == expr);
OuterExpansions.pop_back();
auto expansionType = CS.getType(expr)->castTo<PackExpansionType>();
auto elementResultType = CS.getType(expr->getPatternExpr());
CS.addConstraint(ConstraintKind::PackElementOf, elementResultType,
expansionType->getPatternType(),
CS.getConstraintLocator(expr));
// Generate ShapeOf constraints between all packs expanded by this
// pack expansion expression through the shape type variable.
SmallVector<ASTNode, 2> expandedPacks;
collectExpandedPacks(expr, expandedPacks);
if (expandedPacks.empty()) {
(void)CS.recordFix(AllowValueExpansionWithoutPackReferences::create(
CS, CS.getConstraintLocator(expr)));
}
for (auto pack : expandedPacks) {
Type packType;
/// Skipping over pack elements because the relationship to its
/// environment is now established during \c pushPackExpansionExpr
/// upon visiting its pack expansion and the Shape constraint added
/// upon visiting the pack element.
if (isExpr<PackElementExpr>(pack)) {
continue;
} else if (auto *elementType =
getAsTypeRepr<PackElementTypeRepr>(pack)) {
// OpenPackElementType sets types for 'each T' type reprs in
// expressions. Some invalid code won't make it there, and
// the constraint system won't have recorded a type.
if (!CS.hasType(elementType->getPackType()))
return Type();
packType = CS.getType(elementType->getPackType());
} else {
llvm_unreachable("unsupported pack reference ASTNode");
}
auto *elementShape = CS.createTypeVariable(
CS.getConstraintLocator(pack, ConstraintLocator::PackShape),
TVO_CanBindToPack);
CS.addConstraint(
ConstraintKind::ShapeOf, elementShape, packType,
CS.getConstraintLocator(pack, ConstraintLocator::PackShape));
CS.addConstraint(
ConstraintKind::Equal, elementShape, expansionType->getCountType(),
CS.getConstraintLocator(expr, ConstraintLocator::PackShape));
}
return expansionType;
}
Type visitPackElementExpr(PackElementExpr *expr) {
auto packType = CS.getType(expr->getPackRefExpr());
auto *packExpansion = CS.getPackElementExpansion(expr);
auto elementType = openPackElement(
packType, CS.getConstraintLocator(expr), packExpansion);
if (packExpansion) {
auto expansionType =
CS.getType(packExpansion)->castTo<PackExpansionType>();
CS.addConstraint(ConstraintKind::ShapeOf, expansionType->getCountType(),
packType,
CS.getConstraintLocator(packExpansion,
ConstraintLocator::PackShape));
} else {
CS.recordFix(AllowInvalidPackReference::create(
CS, packType, CS.getConstraintLocator(expr->getPackRefExpr())));
}
return elementType;
}
Type visitMaterializePackExpr(MaterializePackExpr *expr) {
llvm_unreachable("MaterializePackExpr already type-checked");
}
Type visitDynamicTypeExpr(DynamicTypeExpr *expr) {
auto tv = CS.createTypeVariable(CS.getConstraintLocator(expr),
TVO_CanBindToNoEscape);
CS.addConstraint(
ConstraintKind::DynamicTypeOf, tv, CS.getType(expr->getBase()),
CS.getConstraintLocator(expr, ConstraintLocator::DynamicType));
return tv;
}
Type visitOpaqueValueExpr(OpaqueValueExpr *expr) {
return expr->getType();
}
Type visitPropertyWrapperValuePlaceholderExpr(
PropertyWrapperValuePlaceholderExpr *expr) {
if (auto ty = expr->getType()) {
CS.cacheType(expr);
return ty;
}
assert(CS.getType(expr));
return CS.getType(expr);
}
Type visitAppliedPropertyWrapperExpr(AppliedPropertyWrapperExpr *expr) {
return expr->getType();
}
Type visitDefaultArgumentExpr(DefaultArgumentExpr *expr) {
return expr->getType();
}
Type visitApplyExpr(ApplyExpr *expr) {
auto fnExpr = expr->getFn();
CS.associateArgumentList(CS.getConstraintLocator(expr), expr->getArgs());
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(fnExpr)) {
auto typeOperation = getTypeOperation(UDE, CS.getASTContext());
if (typeOperation != TypeOperation::None)
return resultOfTypeOperation(typeOperation, expr->getArgs());
}
// The result type is a fresh type variable.
Type resultType = CS.createTypeVariable(
CS.getConstraintLocator(expr, ConstraintLocator::FunctionResult),
TVO_CanBindToNoEscape);
// A direct call to a ClosureExpr makes it noescape.
FunctionType::ExtInfo extInfo;
if (isa<ClosureExpr>(fnExpr->getSemanticsProvidingExpr()))
extInfo = extInfo.withNoEscape();
SmallVector<AnyFunctionType::Param, 8> params;
getMatchingParams(expr->getArgs(), params);
CS.addApplicationConstraint(
FunctionType::get(params, resultType, extInfo), CS.getType(fnExpr),
/*trailingClosureMatching=*/std::nullopt, CurDC,
CS.getConstraintLocator(expr, ConstraintLocator::ApplyFunction));
// If we ended up resolving the result type variable to a concrete type,
// set it as the favored type for this expression.
Type fixedType =
CS.getFixedTypeRecursive(resultType, /*wantRvalue=*/true);
if (!fixedType->isTypeVariableOrMember()) {
CS.setFavoredType(expr, fixedType.getPointer());
resultType = fixedType;
}
return resultType;
}
Type visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *expr) {
// The result is void.
return TupleType::getEmpty(CS.getASTContext());
}
Type visitTernaryExpr(TernaryExpr *expr) {
// Condition must convert to Bool.
auto boolDecl = CS.getASTContext().getBoolDecl();
if (!boolDecl)
return Type();
CS.addConstraint(
ConstraintKind::Conversion, CS.getType(expr->getCondExpr()),
boolDecl->getDeclaredInterfaceType(),
CS.getConstraintLocator(expr, ConstraintLocator::Condition));
// The branches must be convertible to a common type.
return CS.addJoinConstraint(
CS.getConstraintLocator(expr),
{{CS.getType(expr->getThenExpr()),
CS.getConstraintLocator(expr, LocatorPathElt::TernaryBranch(true))},
{CS.getType(expr->getElseExpr()),
CS.getConstraintLocator(expr,
LocatorPathElt::TernaryBranch(false))}});
}
virtual Type visitImplicitConversionExpr(ImplicitConversionExpr *expr) {
llvm_unreachable("Already type-checked");
}
Type
createTypeVariableAndDisjunctionForIUOCoercion(Type toType,
ConstraintLocator *locator) {
auto typeVar = CS.createTypeVariable(locator, TVO_CanBindToNoEscape);
CS.buildDisjunctionForImplicitlyUnwrappedOptional(typeVar, toType,
locator);
return typeVar;
}
Type getTypeForCast(ExplicitCastExpr *E) {
if (auto *const repr = E->getCastTypeRepr()) {
// Validate the resulting type.
return resolveTypeReferenceInExpression(
repr, TypeResolverContext::ExplicitCastExpr,
CS.getConstraintLocator(E));
}
assert(E->isImplicit());
return E->getCastType();
}
Type visitForcedCheckedCastExpr(ForcedCheckedCastExpr *expr) {
auto fromExpr = expr->getSubExpr();
if (!fromExpr) // Either wasn't constructed correctly or wasn't folded.
return nullptr;
auto toType = getTypeForCast(expr);
if (!toType)
return Type();
auto *const repr = expr->getCastTypeRepr();
// Cache the type we're casting to.
if (repr) CS.setType(repr, toType);
auto fromType = CS.getType(fromExpr);
auto locator = CS.getConstraintLocator(expr);
// The source type can be checked-cast to the destination type.
CS.addConstraint(ConstraintKind::CheckedCast, fromType, toType, locator);
// If the result type was declared IUO, add a disjunction for
// bindings for the result of the coercion.
if (repr && repr->getKind() == TypeReprKind::ImplicitlyUnwrappedOptional)
return createTypeVariableAndDisjunctionForIUOCoercion(toType, locator);
return toType;
}
Type visitCoerceExpr(CoerceExpr *expr) {
// Validate the resulting type.
auto toType = getTypeForCast(expr);
if (!toType)
return nullptr;
auto *const repr = expr->getCastTypeRepr();
// Cache the type we're casting to.
if (repr) CS.setType(repr, toType);
auto fromType = CS.getType(expr->getSubExpr());
auto locator =
CS.getConstraintLocator(expr, ConstraintLocator::CoercionOperand);
// Literal initialization (e.g. `UInt32(0)`) doesn't require
// a conversion because the literal is supposed to assume the
// `to` type.
//
// `to` type could be a type variable if i.e. the repr is invalid,
// in such cases a slower conversion path is a better choice to
// let it be inferred from the context and/or from the literal itself.
if (expr->isLiteralInit() && !toType->isTypeVariableOrMember()) {
CS.addConstraint(ConstraintKind::Equal, fromType, toType, locator);
} else {
// Add a conversion constraint for the direct conversion between
// types.
CS.addExplicitConversionConstraint(fromType, toType, RememberChoice,
locator);
}
// If the result type was declared IUO, add a disjunction for
// bindings for the result of the coercion.
if (repr &&
repr->getKind() == TypeReprKind::ImplicitlyUnwrappedOptional) {
return createTypeVariableAndDisjunctionForIUOCoercion(
toType, CS.getConstraintLocator(expr));
}
return toType;
}
Type visitConditionalCheckedCastExpr(ConditionalCheckedCastExpr *expr) {
auto fromExpr = expr->getSubExpr();
if (!fromExpr) // Either wasn't constructed correctly or wasn't folded.
return nullptr;
// Validate the resulting type.
const auto toType = getTypeForCast(expr);
if (!toType)
return nullptr;
auto *const repr = expr->getCastTypeRepr();
// Cache the type we're casting to.
if (repr) CS.setType(repr, toType);
auto fromType = CS.getType(fromExpr);
auto locator = CS.getConstraintLocator(expr);
CS.addConstraint(ConstraintKind::CheckedCast, fromType, toType, locator);
// If the result type was declared IUO, add a disjunction for
// bindings for the result of the coercion.
if (repr && repr->getKind() == TypeReprKind::ImplicitlyUnwrappedOptional)
return createTypeVariableAndDisjunctionForIUOCoercion(
OptionalType::get(toType), locator);
return OptionalType::get(toType);
}
Type visitIsExpr(IsExpr *expr) {
auto toType = getTypeForCast(expr);
if (!toType)
return nullptr;
auto *const repr = expr->getCastTypeRepr();
// Cache the type we're checking.
if (repr)
CS.setType(repr, toType);
// Add a checked cast constraint.
auto fromType = CS.getType(expr->getSubExpr());
CS.addConstraint(ConstraintKind::CheckedCast, fromType, toType,
CS.getConstraintLocator(expr));
auto &ctx = CS.getASTContext();
// The result is Bool.
auto boolDecl = ctx.getBoolDecl();
if (!boolDecl) {
ctx.Diags.diagnose(SourceLoc(), diag::broken_stdlib_type, "Bool");
return Type();
}
return boolDecl->getDeclaredInterfaceType();
}
Type visitDiscardAssignmentExpr(DiscardAssignmentExpr *expr) {
auto locator = CS.getConstraintLocator(expr);
auto typeVar = CS.createTypeVariable(locator, TVO_CanBindToNoEscape);
return LValueType::get(typeVar);
}
static Type genAssignDestType(Expr *expr, ConstraintSystem &CS) {
if (auto *TE = dyn_cast<TupleExpr>(expr)) {
SmallVector<TupleTypeElt, 4> destTupleTypes;
for (unsigned i = 0; i != TE->getNumElements(); ++i) {
Type subType = genAssignDestType(TE->getElement(i), CS);
destTupleTypes.push_back(TupleTypeElt(subType, TE->getElementName(i)));
}
return TupleType::get(destTupleTypes, CS.getASTContext());
} else {
auto *locator = CS.getConstraintLocator(expr);
auto exprType = CS.getType(expr);
auto *destTy = CS.createTypeVariable(locator, TVO_CanBindToNoEscape);
CS.addConstraint(ConstraintKind::LValueObject, exprType, destTy,
locator);
return destTy;
}
}
Type visitAssignExpr(AssignExpr *expr) {
// Handle invalid code.
if (!expr->getDest() || !expr->getSrc())
return Type();
Type destTy = genAssignDestType(expr->getDest(), CS);
CS.addConstraint(ConstraintKind::Conversion,
CS.getType(expr->getSrc()), destTy,
CS.getConstraintLocator(expr));
return TupleType::getEmpty(CS.getASTContext());
}
Type visitUnresolvedPatternExpr(UnresolvedPatternExpr *expr) {
// Encountering an UnresolvedPatternExpr here means we have an invalid
// ExprPattern with a Pattern node like 'let x' nested in it. Record a
// fix, and assign ErrorTypes to any VarDecls bound.
auto *locator = CS.getConstraintLocator(expr);
auto *P = expr->getSubPattern();
CS.recordFix(IgnoreInvalidPatternInExpr::create(CS, P, locator));
P->forEachVariable([&](VarDecl *VD) {
CS.setType(VD, ErrorType::get(CS.getASTContext()));
});
return CS.createTypeVariable(locator, TVO_CanBindToHole);
}
/// Get the type T?
///
/// This is not the ideal source location, but it's only used for
/// diagnosing ill-formed standard libraries, so it really isn't
/// worth QoI efforts.
Type getOptionalType(SourceLoc optLoc, Type valueTy) {
auto optTy = TypeChecker::getOptionalType(optLoc, valueTy);
if (optTy->hasError() ||
TypeChecker::requireOptionalIntrinsics(CS.getASTContext(), optLoc))
return Type();
return optTy;
}
Type visitBindOptionalExpr(BindOptionalExpr *expr) {
// The operand must be coercible to T?, and we will have type T.
auto locator = CS.getConstraintLocator(expr);
auto objectTy = CS.createTypeVariable(locator,
TVO_PrefersSubtypeBinding |
TVO_CanBindToLValue |
TVO_CanBindToNoEscape);
// The result is the object type of the optional subexpression.
CS.addConstraint(ConstraintKind::OptionalObject,
CS.getType(expr->getSubExpr()), objectTy,
locator);
return objectTy;
}
Type visitOptionalEvaluationExpr(OptionalEvaluationExpr *expr) {
// The operand must be coercible to T? for some type T. We'd
// like this to be the smallest possible nesting level of
// optional types, e.g. T? over T??; otherwise we don't really
// have a preference.
auto valueTy = CS.createTypeVariable(CS.getConstraintLocator(expr),
TVO_PrefersSubtypeBinding |
TVO_CanBindToNoEscape);
Type optTy = getOptionalType(expr->getSubExpr()->getLoc(), valueTy);
if (!optTy)
return Type();
CS.addConstraint(ConstraintKind::Conversion,
CS.getType(expr->getSubExpr()), optTy,
CS.getConstraintLocator(expr));
return optTy;
}
Type visitForceValueExpr(ForceValueExpr *expr) {
// Force-unwrap an optional of type T? to produce a T.
auto locator = CS.getConstraintLocator(expr);
auto options = TVO_CanBindToLValue | TVO_CanBindToNoEscape;
if (isExpr<UnresolvedDotExpr>(expr->getSubExpr()))
options |= TVO_PrefersSubtypeBinding;
auto objectTy = CS.createTypeVariable(locator, options);
auto *valueExpr = expr->getSubExpr();
// It's invalid to force unwrap `nil` literal e.g. `_ = nil!` or
// `_ = (try nil)!` and similar constructs.
if (auto *nilLiteral = dyn_cast<NilLiteralExpr>(
valueExpr->getSemanticsProvidingExpr())) {
CS.recordFix(SpecifyContextualTypeForNil::create(
CS, CS.getConstraintLocator(nilLiteral)));
}
// The result is the object type of the optional subexpression.
CS.addConstraint(ConstraintKind::OptionalObject, CS.getType(valueExpr),
objectTy, locator);
return objectTy;
}
Type visitOpenExistentialExpr(OpenExistentialExpr *expr) {
llvm_unreachable("Already type-checked");
}
Type visitMakeTemporarilyEscapableExpr(MakeTemporarilyEscapableExpr *expr) {
llvm_unreachable("Already type-checked");
}
Type visitKeyPathApplicationExpr(KeyPathApplicationExpr *expr) {
// This should only appear in already-type-checked solutions, but we may
// need to re-check for failure diagnosis.
auto locator = CS.getConstraintLocator(expr);
auto projectedTy = CS.createTypeVariable(locator,
TVO_CanBindToLValue |
TVO_CanBindToNoEscape);
CS.addKeyPathApplicationConstraint(CS.getType(expr->getKeyPath()),
CS.getType(expr->getBase()),
projectedTy,
locator);
return projectedTy;
}
Type visitEnumIsCaseExpr(EnumIsCaseExpr *expr) {
return CS.getASTContext().getBoolType();
}
Type visitLazyInitializerExpr(LazyInitializerExpr *expr) {
llvm_unreachable("Already type-checked");
}
Type visitEditorPlaceholderExpr(EditorPlaceholderExpr *E) {
auto *locator = CS.getConstraintLocator(E);
if (auto *placeholderRepr = E->getPlaceholderTypeRepr()) {
// Let's try to use specified type, if that's impossible,
// fallback to a type variable.
if (auto preferredTy = resolveTypeReferenceInExpression(
placeholderRepr, TypeResolverContext::InExpression, locator))
return preferredTy;
}
// A placeholder may have any type, but default to Void type if
// otherwise unconstrained.
auto *placeholderTy =
CS.createTypeVariable(locator, TVO_CanBindToNoEscape);
CS.addConstraint(ConstraintKind::Defaultable, placeholderTy,
TupleType::getEmpty(CS.getASTContext()), locator);
return placeholderTy;
}
Type visitObjCSelectorExpr(ObjCSelectorExpr *E) {
// #selector only makes sense when we have the Objective-C
// runtime.
auto &ctx = CS.getASTContext();
if (!ctx.LangOpts.EnableObjCInterop) {
ctx.Diags.diagnose(E->getLoc(), diag::expr_selector_no_objc_runtime);
return nullptr;
}
// Make sure we can reference ObjectiveC.Selector.
// FIXME: Fix-It to add the import?
auto type = CS.getASTContext().getSelectorType();
if (!type) {
ctx.Diags.diagnose(E->getLoc(), diag::expr_selector_module_missing);
return nullptr;
}
return type;
}
Type visitKeyPathExpr(KeyPathExpr *E) {
if (E->isObjC())
return CS.getType(E->getObjCStringLiteralExpr());
auto kpDecl = CS.getASTContext().getKeyPathDecl();
if (!kpDecl) {
auto &de = CS.getASTContext().Diags;
de.diagnose(E->getLoc(), diag::expr_keypath_no_keypath_type);
return ErrorType::get(CS.getASTContext());
}
// For native key paths, traverse the key path components to set up
// appropriate type relationships at each level.
auto rootLocator =
CS.getConstraintLocator(E, ConstraintLocator::KeyPathRoot);
auto locator = CS.getConstraintLocator(E);
auto *root = CS.createTypeVariable(rootLocator, TVO_CanBindToNoEscape |
TVO_CanBindToHole);
// If a root type was explicitly given, then resolve it now.
if (auto rootRepr = E->getExplicitRootType()) {
const auto rootObjectTy = resolveTypeReferenceInExpression(
rootRepr, TypeResolverContext::InExpression, locator);
if (!rootObjectTy || rootObjectTy->hasError())
return Type();
CS.setType(rootRepr, rootObjectTy);
// Allow \Derived.property to be inferred as \Base.property to
// simulate a sort of covariant conversion from
// KeyPath<Derived, T> to KeyPath<Base, T>.
CS.addConstraint(ConstraintKind::Subtype, rootObjectTy, root,
rootLocator);
}
bool didOptionalChain = false;
// We start optimistically from an lvalue base.
Type base = LValueType::get(root);
SmallVector<TypeVariableType *, 2> componentTypeVars;
for (unsigned i : indices(E->getComponents())) {
auto &component = E->getComponents()[i];
auto memberLocator = CS.getConstraintLocator(
locator, LocatorPathElt::KeyPathComponent(i));
auto resultLocator = CS.getConstraintLocator(
memberLocator, ConstraintLocator::KeyPathComponentResult);
switch (auto kind = component.getKind()) {
case KeyPathExpr::Component::Kind::Invalid:
break;
case KeyPathExpr::Component::Kind::CodeCompletion:
// We don't know what the code completion might resolve to, so we are
// creating a new type variable for its result, which might be a hole.
base = CS.createTypeVariable(
resultLocator,
TVO_CanBindToLValue | TVO_CanBindToNoEscape | TVO_CanBindToHole);
break;
case KeyPathExpr::Component::Kind::UnresolvedMember:
// This should only appear in resolved ASTs, but we may need to
// re-type-check the constraints during failure diagnosis.
case KeyPathExpr::Component::Kind::Member: {
auto memberTy = CS.createTypeVariable(resultLocator,
TVO_CanBindToLValue |
TVO_CanBindToNoEscape);
componentTypeVars.push_back(memberTy);
auto lookupName =
kind == KeyPathExpr::Component::Kind::UnresolvedMember
? DeclNameRef(
component.getUnresolvedDeclName()) // FIXME: type change
// needed
: component.getDeclRef().getDecl()->createNameRef();
auto refKind = component.getFunctionRefInfo();
CS.addValueMemberConstraint(base, lookupName, memberTy, CurDC,
refKind,
/*outerAlternatives=*/{}, memberLocator);
base = memberTy;
break;
}
case KeyPathExpr::Component::Kind::UnresolvedSubscript:
// Subscript should only appear in resolved ASTs, but we may need to
// re-type-check the constraints during failure diagnosis.
case KeyPathExpr::Component::Kind::Subscript: {
auto *args = component.getArgs();
base = addSubscriptConstraints(E, base, /*decl*/ nullptr, args,
memberLocator, &componentTypeVars);
auto &ctx = CS.getASTContext();
// All of the type variables that appear in subscript arguments
// need to be connected to a key path, otherwise it won't be
// possible to determine sendability of the key path type if
// the arguments are disconnected from it before being fully
// resolved.
if (args &&
ctx.LangOpts.hasFeature(Feature::InferSendableFromCaptures)) {
SmallPtrSet<TypeVariableType *, 2> referencedVars;
for (const auto &arg : *args) {
CS.getType(arg.getExpr())->getTypeVariables(referencedVars);
}
componentTypeVars.append(referencedVars.begin(),
referencedVars.end());
}
break;
}
case KeyPathExpr::Component::Kind::UnresolvedApply:
case KeyPathExpr::Component::Kind::Apply: {
auto prevMemberLocator = CS.getConstraintLocator(
locator, LocatorPathElt::KeyPathComponent(i - 1));
base = addApplyConstraints(E, base, component.getArgs(),
prevMemberLocator, memberLocator,
&componentTypeVars);
break;
}
case KeyPathExpr::Component::Kind::TupleElement: {
// Note: If implemented, the logic in `getCalleeLocator` will need
// updating to return the correct callee locator for this.
llvm_unreachable("not implemented");
break;
}
case KeyPathExpr::Component::Kind::OptionalChain: {
didOptionalChain = true;
// We can't assign an optional back through an optional chain
// today. Force the base to an rvalue.
auto rvalueTy = CS.createTypeVariable(resultLocator,
TVO_CanBindToNoEscape);
componentTypeVars.push_back(rvalueTy);
CS.addConstraint(ConstraintKind::Equal, base, rvalueTy,
resultLocator);
base = rvalueTy;
LLVM_FALLTHROUGH;
}
case KeyPathExpr::Component::Kind::OptionalForce: {
auto optionalObjTy = CS.createTypeVariable(resultLocator,
TVO_CanBindToLValue |
TVO_CanBindToNoEscape);
componentTypeVars.push_back(optionalObjTy);
CS.addConstraint(ConstraintKind::OptionalObject, base, optionalObjTy,
resultLocator);
base = optionalObjTy;
break;
}
case KeyPathExpr::Component::Kind::OptionalWrap: {
// This should only appear in resolved ASTs, but we may need to
// re-type-check the constraints during failure diagnosis.
base = OptionalType::get(base);
break;
}
case KeyPathExpr::Component::Kind::Identity:
break;
case KeyPathExpr::Component::Kind::DictionaryKey:
llvm_unreachable("DictionaryKey only valid in #keyPath");
break;
}
// By now, `base` is the result type of this component. Set it in the
// constraint system so we can find it later.
CS.setType(E, i, base);
}
auto valueLocator =
CS.getConstraintLocator(E, ConstraintLocator::KeyPathValue);
// If there was an optional chaining component, the end result must be
// optional.
if (didOptionalChain) {
auto objTy = CS.createTypeVariable(locator, TVO_CanBindToNoEscape |
TVO_CanBindToHole);
componentTypeVars.push_back(objTy);
auto optTy = OptionalType::get(objTy);
CS.addConstraint(ConstraintKind::Conversion, base, optTy, valueLocator);
base = optTy;
}
// If we have a malformed KeyPathExpr e.g. let _: KeyPath<A, C> = \A
// let's record a AllowKeyPathMissingComponent fix.
if (E->hasSingleInvalidComponent()) {
(void)CS.recordFix(AllowKeyPathWithoutComponents::create(CS, locator));
}
auto *value = CS.createTypeVariable(valueLocator, TVO_CanBindToNoEscape |
TVO_CanBindToHole);
CS.addConstraint(ConstraintKind::Equal, base, value, valueLocator);
CS.recordKeyPath(E, root, value, CurDC);
// The result is a KeyPath from the root to the end component.
// The type of key path depends on the overloads chosen for the key
// path components.
auto typeLoc =
CS.getConstraintLocator(locator, LocatorPathElt::KeyPathType());
Type kpTy = CS.createTypeVariable(typeLoc, TVO_CanBindToNoEscape |
TVO_CanBindToHole);
CS.addKeyPathConstraint(kpTy, root, value, componentTypeVars, locator);
// Add a fallback constraint so we have an anchor to use for
// capability inference when there is no context.
CS.addUnsolvedConstraint(Constraint::create(
CS, ConstraintKind::FallbackType, kpTy,
BoundGenericType::get(kpDecl, /*parent=*/Type(), {root, value}),
CS.getConstraintLocator(E, ConstraintLocator::FallbackType)));
return kpTy;
}
Type visitCurrentContextIsolationExpr(CurrentContextIsolationExpr *E) {
// If this was expanded from the builtin `#isolation` macro, it
// already has a type.
if (auto type = E->getType())
return type;
// Otherwise, this was created for a `for await` loop, where its
// type is always `(any Actor)?`.
auto actorProto = CS.getASTContext().getProtocol(
KnownProtocolKind::Actor);
return OptionalType::get(actorProto->getDeclaredExistentialType());
}
Type visitExtractFunctionIsolationExpr(ExtractFunctionIsolationExpr *E) {
llvm_unreachable("found ExtractFunctionIsolationExpr in CSGen");
}
Type visitKeyPathDotExpr(KeyPathDotExpr *E) {
llvm_unreachable("found KeyPathDotExpr in CSGen");
}
Type visitSingleValueStmtExpr(SingleValueStmtExpr *E) {
llvm_unreachable("Handled by the walker directly");
}
Type visitTapExpr(TapExpr *expr) {
DeclContext *varDC = expr->getVar()->getDeclContext();
ASSERT(varDC != nullptr);
ASSERT((varDC == CS.DC ||
isa<AbstractClosureExpr>(varDC) ||
varDC->isChildContextOf(CS.DC)) &&
"TapExpr var should be in the same DeclContext we're checking it in!");
auto locator = CS.getConstraintLocator(expr);
auto tv = CS.createTypeVariable(locator, TVO_CanBindToNoEscape);
if (auto subExpr = expr->getSubExpr()) {
auto subExprType = CS.getType(subExpr);
CS.addConstraint(ConstraintKind::Bind, subExprType, tv, locator);
}
return tv;
}
Type visitTypeJoinExpr(TypeJoinExpr *expr) {
auto *locator = CS.getConstraintLocator(expr);
SmallVector<std::pair<Type, ConstraintLocator *>, 4> elements;
elements.reserve(expr->getNumElements());
if (auto *SVE = expr->getSingleValueStmtExpr()) {
// If we have a SingleValueStmtExpr, form a join of the branch types.
SmallVector<Expr *, 4> scratch;
auto branches = SVE->getResultExprs(scratch);
for (auto idx : indices(branches)) {
auto *eltLoc = CS.getConstraintLocator(
SVE, {LocatorPathElt::SingleValueStmtResult(idx)});
elements.emplace_back(CS.getType(branches[idx]), eltLoc);
}
} else {
for (auto *element : expr->getElements()) {
elements.emplace_back(CS.getType(element),
CS.getConstraintLocator(element));
}
}
Type resultTy;
if (auto *var = expr->getVar()) {
resultTy = CS.getType(var);
} else {
resultTy = expr->getType();
}
assert(resultTy);
// If we have a single branch of a SingleValueStmtExpr, we want a
// conversion of the result, not a join, which would skip the conversion.
// This is needed to ensure we apply the Void/Never conversions.
if (elements.size() == 1 && expr->getSingleValueStmtExpr()) {
auto &elt = elements[0];
CS.addConstraint(ConstraintKind::Conversion, elt.first,
resultTy, elt.second);
return resultTy;
}
// The type of a join expression is obtained by performing
// a "join-meet" operation on deduced types of its elements
// and the underlying variable.
auto joinedTy = CS.addJoinConstraint(locator, elements);
CS.addConstraint(ConstraintKind::Equal, resultTy, joinedTy, locator);
return resultTy;
}
/// Lookup all macros with the given macro name.
SmallVector<OverloadChoice, 1> lookupMacros(Identifier moduleName,
Identifier macroName,
FunctionRefInfo functionRefInfo,
MacroRoles roles) {
SmallVector<OverloadChoice, 1> choices;
auto results = namelookup::lookupMacros(CurDC, DeclNameRef(moduleName),
DeclNameRef(macroName), roles);
for (const auto &result : results) {
// Ignore invalid results. This matches the OverloadedDeclRefExpr
// logic.
if (result->isInvalid())
continue;
OverloadChoice choice = OverloadChoice(Type(), result, functionRefInfo);
choices.push_back(choice);
}
// FIXME: At some point, we need to check for function-like macros without
// arguments and vice-versa.
return choices;
}
Type visitMacroExpansionExpr(MacroExpansionExpr *expr) {
// Assign a discriminator.
(void)expr->getDiscriminator();
auto &ctx = CS.getASTContext();
auto locator = CS.getConstraintLocator(expr);
CS.associateArgumentList(locator, expr->getArgs());
// Look up the macros with this name.
auto moduleIdent = expr->getModuleName().getBaseIdentifier();
auto macroIdent = expr->getMacroName().getBaseIdentifier();
FunctionRefInfo functionRefInfo = FunctionRefInfo::singleBaseNameApply();
auto macros = lookupMacros(moduleIdent, macroIdent, functionRefInfo,
expr->getMacroRoles());
if (macros.empty()) {
ctx.Diags.diagnose(expr->getMacroNameLoc(), diag::macro_undefined,
macroIdent)
.highlight(expr->getMacroNameLoc().getSourceRange());
return Type();
}
// Introduce an overload set for the macro reference.
auto macroRefType = Type(CS.createTypeVariable(locator, 0));
CS.addOverloadSet(macroRefType, macros, CurDC, locator);
// Add explicit generic arguments, if there were any.
if (expr->getGenericArgsRange().isValid()) {
addSpecializationConstraint(CS.getConstraintLocator(expr), macroRefType,
expr->getGenericArgsRange().Start,
expr->getGenericArgs());
}
// Form the applicable-function constraint. The result type
// is the result of that call.
SmallVector<AnyFunctionType::Param, 8> params;
getMatchingParams(expr->getArgs(), params);
Type resultType = CS.createTypeVariable(
CS.getConstraintLocator(expr, ConstraintLocator::FunctionResult),
TVO_CanBindToNoEscape);
CS.addApplicationConstraint(
FunctionType::get(params, resultType),
macroRefType,
/*trailingClosureMatching=*/std::nullopt,
CurDC,
CS.getConstraintLocator(
expr, ConstraintLocator::ApplyFunction));
return resultType;
}
static bool isTriggerFallbackDiagnosticBuiltin(UnresolvedDotExpr *UDE,
ASTContext &Context) {
auto *DRE = dyn_cast<DeclRefExpr>(UDE->getBase());
if (!DRE)
return false;
if (DRE->getDecl() != Context.TheBuiltinModule)
return false;
auto member = UDE->getName().getBaseName().userFacingName();
return member == "trigger_fallback_diagnostic";
}
enum class TypeOperation { None,
Join,
JoinInout,
JoinMeta,
JoinNonexistent,
};
static TypeOperation getTypeOperation(UnresolvedDotExpr *UDE,
ASTContext &Context) {
auto *DRE = dyn_cast<DeclRefExpr>(UDE->getBase());
if (!DRE)
return TypeOperation::None;
if (DRE->getDecl() != Context.TheBuiltinModule)
return TypeOperation::None;
return llvm::StringSwitch<TypeOperation>(
UDE->getName().getBaseIdentifier().str())
.Case("type_join", TypeOperation::Join)
.Case("type_join_inout", TypeOperation::JoinInout)
.Case("type_join_meta", TypeOperation::JoinMeta)
.Case("type_join_nonexistent", TypeOperation::JoinNonexistent)
.Default(TypeOperation::None);
}
Type resultOfTypeOperation(TypeOperation op, ArgumentList *Args) {
auto *lhs = Args->getExpr(0);
auto *rhs = Args->getExpr(1);
switch (op) {
case TypeOperation::None:
llvm_unreachable(
"We should have a valid type operation at this point!");
case TypeOperation::Join: {
auto lhsMeta = CS.getType(lhs)->getAs<MetatypeType>();
auto rhsMeta = CS.getType(rhs)->getAs<MetatypeType>();
if (!lhsMeta || !rhsMeta)
llvm_unreachable("Unexpected argument types for Builtin.type_join!");
auto &ctx = lhsMeta->getASTContext();
auto join =
Type::join(lhsMeta->getInstanceType(), rhsMeta->getInstanceType());
if (!join)
return ErrorType::get(ctx);
return MetatypeType::get(*join, ctx)->getCanonicalType();
}
case TypeOperation::JoinInout: {
auto lhsInOut = CS.getType(lhs)->getAs<InOutType>();
auto rhsMeta = CS.getType(rhs)->getAs<MetatypeType>();
if (!lhsInOut || !rhsMeta)
llvm_unreachable("Unexpected argument types for Builtin.type_join!");
auto &ctx = lhsInOut->getASTContext();
auto join =
Type::join(lhsInOut, rhsMeta->getInstanceType());
if (!join)
return ErrorType::get(ctx);
return MetatypeType::get(*join, ctx)->getCanonicalType();
}
case TypeOperation::JoinMeta: {
auto lhsMeta = CS.getType(lhs)->getAs<MetatypeType>();
auto rhsMeta = CS.getType(rhs)->getAs<MetatypeType>();
if (!lhsMeta || !rhsMeta)
llvm_unreachable("Unexpected argument types for Builtin.type_join!");
auto &ctx = lhsMeta->getASTContext();
auto join = Type::join(lhsMeta, rhsMeta);
if (!join)
return ErrorType::get(ctx);
return *join;
}
case TypeOperation::JoinNonexistent: {
auto lhsMeta = CS.getType(lhs)->getAs<MetatypeType>();
auto rhsMeta = CS.getType(rhs)->getAs<MetatypeType>();
if (!lhsMeta || !rhsMeta)
llvm_unreachable("Unexpected argument types for Builtin.type_join_nonexistent!");
auto &ctx = lhsMeta->getASTContext();
auto join =
Type::join(lhsMeta->getInstanceType(), rhsMeta->getInstanceType());
// Verify that we could not compute a join.
if (join)
llvm_unreachable("Unexpected result from join - it should not have been computable!");
// The return value is unimportant.
return MetatypeType::get(ctx.getAnyExistentialType())->getCanonicalType();
}
}
llvm_unreachable("unhandled operation");
}
/// Assuming that we are solving for code completion, assign \p expr a fresh
/// and unconstrained type variable as its type.
void setTypeForArgumentIgnoredForCompletion(Expr *expr) {
assert(CS.isForCodeCompletion());
ConstraintSystem &CS = getConstraintSystem();
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
FunctionType *closureTy =
inferClosureType(closure, /*allowResultBindToHole=*/true);
CS.setClosureType(closure, closureTy);
CS.setType(closure, closureTy);
} else {
TypeVariableType *exprType = CS.createTypeVariable(
CS.getConstraintLocator(expr),
TVO_CanBindToLValue | TVO_CanBindToInOut | TVO_CanBindToNoEscape |
TVO_CanBindToHole);
CS.setType(expr, exprType);
}
}
};
class ConstraintWalker : public ASTWalker {
ConstraintGenerator &CG;
public:
ConstraintWalker(ConstraintGenerator &CG) : CG(CG) { }
MacroWalking getMacroWalkingBehavior() const override {
return MacroWalking::Arguments;
}
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
auto &CS = CG.getConstraintSystem();
if (CS.isArgumentIgnoredForCodeCompletion(expr)) {
CG.setTypeForArgumentIgnoredForCompletion(expr);
return Action::SkipNode(expr);
}
if (auto *SVE = dyn_cast<SingleValueStmtExpr>(expr)) {
if (CS.generateConstraints(SVE))
return Action::Stop();
return Action::SkipNode(expr);
}
// Note that the subexpression of a #selector expression is
// unevaluated.
if (auto sel = dyn_cast<ObjCSelectorExpr>(expr)) {
auto *subExpr = sel->getSubExpr()->getSemanticsProvidingExpr();
CG.getConstraintSystem().UnevaluatedRootExprs.insert(subExpr);
}
// Check an objc key-path expression, which fills in its semantic
// expression as a string literal.
if (auto keyPath = dyn_cast<KeyPathExpr>(expr)) {
if (keyPath->isObjC()) {
auto &cs = CG.getConstraintSystem();
(void)TypeChecker::checkObjCKeyPathExpr(cs.DC, keyPath);
}
}
// Generate constraints for each of the entries in the capture list.
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
TypeChecker::diagnoseDuplicateCaptureVars(captureList);
}
// Both multi- and single-statement closures now behave the same way
// when it comes to constraint generation.
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
auto &CS = CG.getConstraintSystem();
auto closureType = CG.visitClosureExpr(closure);
if (!closureType)
return Action::Stop();
CS.setType(expr, closureType);
return Action::SkipNode(expr);
}
// Don't visit CoerceExpr with an empty sub expression. They may occur
// if the body of a closure was not visited while pre-checking because
// of an error in the closure's signature.
if (auto coerceExpr = dyn_cast<CoerceExpr>(expr)) {
if (!coerceExpr->getSubExpr()) {
return Action::SkipNode(expr);
}
}
// Don't visit TernaryExpr with empty sub expressions. They may occur
// if the body of a closure was not visited while pre-checking because
// of an error in the closure's signature.
if (auto *ternary = dyn_cast<TernaryExpr>(expr)) {
if (!ternary->getThenExpr() || !ternary->getElseExpr())
return Action::SkipNode(expr);
}
if (CS.isForCodeCompletion()) {
SmallVector<Expr *, 2> ignoredArgs;
getArgumentsAfterCodeCompletionToken(expr, CS, ignoredArgs);
for (auto ignoredArg : ignoredArgs) {
CS.markArgumentIgnoredForCodeCompletion(ignoredArg);
}
}
if (auto *expansion = dyn_cast<PackExpansionExpr>(expr)) {
CG.pushPackExpansionExpr(expansion);
}
return Action::Continue(expr);
}
/// Once we've visited the children of the given expression,
/// generate constraints from the expression.
PostWalkResult<Expr *> walkToExprPost(Expr *expr) override {
auto &CS = CG.getConstraintSystem();
// Translate special type-checker Builtin calls into simpler expressions.
if (auto *apply = dyn_cast<ApplyExpr>(expr)) {
auto fnExpr = apply->getFn();
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(fnExpr)) {
auto typeOperation =
ConstraintGenerator::getTypeOperation(UDE, CS.getASTContext());
if (typeOperation !=
ConstraintGenerator::TypeOperation::None) {
// Handle the Builtin.type_join* family of calls by replacing
// them with dot_self_expr of type_expr with the type being the
// result of the join.
auto joinMetaTy =
CG.resultOfTypeOperation(typeOperation, apply->getArgs());
auto joinTy = joinMetaTy->castTo<MetatypeType>();
auto *TE = TypeExpr::createImplicit(joinTy->getInstanceType(),
CS.getASTContext());
CS.cacheType(TE);
auto *DSE = new (CS.getASTContext())
DotSelfExpr(TE, SourceLoc(), SourceLoc(), CS.getType(TE));
DSE->setImplicit();
CS.cacheType(DSE);
return Action::Continue(DSE);
}
}
}
if (auto type = CG.visit(expr)) {
auto simplifiedType = CS.simplifyType(type);
CS.setType(expr, simplifiedType);
return Action::Continue(expr);
}
return Action::Stop();
}
/// Ignore statements.
PreWalkResult<Stmt *> walkToStmtPre(Stmt *stmt) override {
return Action::SkipNode(stmt);
}
/// Ignore declarations.
PreWalkAction walkToDeclPre(Decl *decl) override {
return Action::SkipNode();
}
};
} // end anonymous namespace
static Expr *generateConstraintsFor(ConstraintSystem &cs, Expr *expr,
DeclContext *DC) {
// Walk the expression, generating constraints.
ConstraintGenerator cg(cs, DC);
ConstraintWalker cw(cg);
Expr *result = expr->walk(cw);
if (result)
cs.optimizeConstraints(result);
return result;
}
bool ConstraintSystem::generateWrappedPropertyTypeConstraints(
VarDecl *wrappedVar, Type initializerType, Type propertyType) {
Type wrappedValueType;
Type wrapperType;
auto wrapperAttributes = wrappedVar->getAttachedPropertyWrappers();
for (unsigned i : indices(wrapperAttributes)) {
// FIXME: We should somehow pass an OpenUnboundGenericTypeFn to
// AttachedPropertyWrapperTypeRequest::evaluate to open up unbound
// generics on the fly.
Type rawWrapperType = wrappedVar->getAttachedPropertyWrapperType(i);
auto wrapperInfo = wrappedVar->getAttachedPropertyWrapperTypeInfo(i);
if (rawWrapperType->hasError() || !wrapperInfo)
return true;
auto *typeExpr = wrapperAttributes[i]->getTypeExpr();
if (!wrappedValueType) {
// Equate the outermost wrapper type to the initializer type.
auto *locator = getConstraintLocator(typeExpr);
wrapperType =
replaceInferableTypesWithTypeVars(rawWrapperType, locator);
if (initializerType)
addConstraint(ConstraintKind::Equal, wrapperType, initializerType, locator);
} else {
// The former wrappedValue type must be equal to the current wrapper type
auto *locator = getConstraintLocator(
typeExpr, LocatorPathElt::WrappedValue(wrapperType));
wrapperType =
replaceInferableTypesWithTypeVars(rawWrapperType, locator);
addConstraint(ConstraintKind::Equal, wrapperType, wrappedValueType, locator);
}
setType(typeExpr, wrapperType);
wrappedValueType = wrapperType->getTypeOfMember(
wrapperInfo.valueVar);
}
// The property type must be equal to the wrapped value type
addConstraint(
ConstraintKind::Equal, propertyType, wrappedValueType,
getConstraintLocator(
wrappedVar, LocatorPathElt::ContextualType(CTP_WrappedProperty)));
ContextualTypeInfo contextInfo(wrappedValueType, CTP_WrappedProperty);
setContextualInfo(wrappedVar, contextInfo);
return false;
}
/// Generate additional constraints for the pattern of an initialization.
static bool generateInitPatternConstraints(ConstraintSystem &cs,
SyntacticElementTarget target,
Expr *initializer) {
auto locator = cs.getConstraintLocator(
initializer, LocatorPathElt::ContextualType(CTP_Initialization));
Type patternType;
if (auto pattern = target.getInitializationPattern()) {
patternType = cs.generateConstraints(
pattern, locator, target.shouldBindPatternVarsOneWay(),
target.getInitializationPatternBindingDecl(),
target.getInitializationPatternBindingIndex());
} else {
patternType = cs.createTypeVariable(locator, TVO_CanBindToNoEscape);
}
if (auto wrappedVar = target.getInitializationWrappedVar())
return cs.generateWrappedPropertyTypeConstraints(
wrappedVar, cs.getType(target.getAsExpr()), patternType);
// Add a conversion constraint between the types.
cs.addConstraint(ConstraintKind::Conversion, cs.getType(target.getAsExpr()),
patternType, locator, /*isFavored*/true);
return false;
}
/// Generate constraints for a for-in statement preamble where the expression
/// is a `PackExpansionExpr`.
static std::optional<PackIterationInfo>
generateForEachStmtConstraints(ConstraintSystem &cs, DeclContext *dc,
PackExpansionExpr *expansion, Type patternType) {
auto packIterationInfo = PackIterationInfo();
auto elementLocator = cs.getConstraintLocator(
expansion, ConstraintLocator::SequenceElementType);
{
SyntacticElementTarget target(expansion, dc, CTP_Unused,
/*contextualType=*/Type(),
/*isDiscarded=*/false);
if (cs.generateConstraints(target))
return std::nullopt;
cs.setTargetFor(expansion, target);
}
auto elementType = cs.getType(expansion->getPatternExpr());
cs.addConstraint(ConstraintKind::Conversion, elementType, patternType,
elementLocator);
packIterationInfo.patternType = patternType;
return packIterationInfo;
}
/// Generate constraints for a for-in statement preamble, expecting an
/// expression that conforms to `Swift.Sequence`.
static std::optional<SequenceIterationInfo>
generateForEachStmtConstraints(ConstraintSystem &cs, DeclContext *dc,
ForEachStmt *stmt, Pattern *typeCheckedPattern,
bool shouldBindPatternVarsOneWay) {
ASTContext &ctx = cs.getASTContext();
bool isAsync = stmt->getAwaitLoc().isValid();
auto *sequenceExpr = stmt->getParsedSequence();
// If we have an unsafe expression for the sequence, lift it out of the
// sequence expression. We'll put it back after we've introduced the
// various calls.
UnsafeExpr *unsafeExpr = dyn_cast<UnsafeExpr>(sequenceExpr);
if (unsafeExpr) {
sequenceExpr = unsafeExpr->getSubExpr();
}
auto contextualLocator = cs.getConstraintLocator(
sequenceExpr, LocatorPathElt::ContextualType(CTP_ForEachSequence));
auto elementLocator = cs.getConstraintLocator(
sequenceExpr, ConstraintLocator::SequenceElementType);
auto sequenceIterationInfo = SequenceIterationInfo();
// The expression type must conform to the Sequence protocol.
auto sequenceProto = TypeChecker::getProtocol(
cs.getASTContext(), stmt->getForLoc(),
isAsync ? KnownProtocolKind::AsyncSequence : KnownProtocolKind::Sequence);
if (!sequenceProto)
return std::nullopt;
std::string name;
{
if (auto np = dyn_cast_or_null<NamedPattern>(stmt->getPattern()))
name = "$"+np->getBoundName().str().str();
name += "$generator";
}
auto *makeIteratorVar = new (ctx)
VarDecl(/*isStatic=*/false, VarDecl::Introducer::Var,
sequenceExpr->getStartLoc(), ctx.getIdentifier(name), dc);
makeIteratorVar->setImplicit();
// FIXME: Apply `nonisolated(unsafe)` to async iterators.
//
// Async iterators are not `Sendable`; they're only meant to be used from
// the isolation domain that creates them. But the `next()` method runs on
// the generic executor, so calling it from an actor-isolated context passes
// non-`Sendable` state across the isolation boundary. `next()` should
// inherit the isolation of the caller, but for now, use the opt out.
if (isAsync) {
auto *nonisolated =
NonisolatedAttr::createImplicit(ctx, NonIsolatedModifier::Unsafe);
makeIteratorVar->getAttrs().add(nonisolated);
}
// First, let's form a call from sequence to `.makeIterator()` and save
// that in a special variable which is going to be used by SILGen.
{
FuncDecl *makeIterator = isAsync ? ctx.getAsyncSequenceMakeAsyncIterator()
: ctx.getSequenceMakeIterator();
auto *makeIteratorRef = new (ctx) UnresolvedDotExpr(
sequenceExpr, SourceLoc(), DeclNameRef(makeIterator->getName()),
DeclNameLoc(stmt->getForLoc()), /*implicit=*/true);
makeIteratorRef->setFunctionRefInfo(FunctionRefInfo::singleBaseNameApply());
Expr *makeIteratorCall =
CallExpr::createImplicitEmpty(ctx, makeIteratorRef);
// Swap in the 'unsafe' expression.
if (unsafeExpr) {
unsafeExpr->setSubExpr(makeIteratorCall);
makeIteratorCall = unsafeExpr;
}
Pattern *pattern = NamedPattern::createImplicit(ctx, makeIteratorVar);
auto *PB = PatternBindingDecl::createImplicit(
ctx, StaticSpellingKind::None, pattern, makeIteratorCall, dc);
auto makeIteratorTarget = SyntacticElementTarget::forInitialization(
makeIteratorCall, /*patternType=*/Type(), PB, /*index=*/0,
/*shouldBindPatternsOneWay=*/false);
ContextualTypeInfo contextInfo(sequenceProto->getDeclaredInterfaceType(),
CTP_ForEachSequence);
cs.setContextualInfo(sequenceExpr, contextInfo);
if (cs.generateConstraints(makeIteratorTarget))
return std::nullopt;
sequenceIterationInfo.makeIteratorVar = PB;
// Type of sequence expression has to conform to Sequence protocol.
//
// Note that the following emulates having `$generator` separately
// type-checked by introducing a `TVO_PrefersSubtypeBinding` type
// variable that would make sure that result of `.makeIterator` would
// get ranked standalone.
{
auto *externalIteratorType = cs.createTypeVariable(
cs.getConstraintLocator(sequenceExpr), TVO_PrefersSubtypeBinding);
cs.addConstraint(ConstraintKind::Equal, externalIteratorType,
cs.getType(sequenceExpr),
externalIteratorType->getImpl().getLocator());
cs.addConstraint(ConstraintKind::ConformsTo, externalIteratorType,
sequenceProto->getDeclaredInterfaceType(),
contextualLocator);
sequenceIterationInfo.sequenceType = cs.getType(sequenceExpr);
}
cs.setTargetFor({PB, /*index=*/0}, makeIteratorTarget);
}
// Now, result type of `.makeIterator()` is used to form a call to
// `.next()`. `next()` is called on each iteration of the loop.
{
FuncDecl *nextFn =
TypeChecker::getForEachIteratorNextFunction(dc, stmt->getForLoc(), isAsync);
Identifier nextId = nextFn ? nextFn->getName().getBaseIdentifier()
: ctx.Id_next;
TinyPtrVector<Identifier> labels;
if (nextFn && nextFn->getParameters()->size() == 1)
labels.push_back(ctx.Id_isolation);
auto *makeIteratorVarRef =
new (ctx) DeclRefExpr(makeIteratorVar, DeclNameLoc(stmt->getForLoc()),
/*Implicit=*/true);
auto *nextRef = new (ctx)
UnresolvedDotExpr(makeIteratorVarRef, SourceLoc(),
DeclNameRef(DeclName(ctx, nextId, labels)),
DeclNameLoc(stmt->getForLoc()), /*implicit=*/true);
nextRef->setFunctionRefInfo(FunctionRefInfo::singleBaseNameApply());
ArgumentList *nextArgs;
if (nextFn && nextFn->getParameters()->size() == 1) {
auto isolationArg =
new (ctx) CurrentContextIsolationExpr(stmt->getForLoc(), Type());
nextArgs = ArgumentList::createImplicit(
ctx, {Argument(SourceLoc(), ctx.Id_isolation, isolationArg)});
} else {
nextArgs = ArgumentList::createImplicit(ctx, {});
}
Expr *nextCall = CallExpr::createImplicit(ctx, nextRef, nextArgs);
// `next` is always async but witness might not be throwing
if (isAsync) {
nextCall =
AwaitExpr::createImplicit(ctx, nextCall->getLoc(), nextCall);
}
// Wrap the 'next' call in 'unsafe', if the for..in loop has that
// effect or if the loop is async (in which case the iterator variable
// is nonisolated(unsafe).
if (stmt->getUnsafeLoc().isValid() ||
(isAsync &&
ctx.LangOpts.StrictConcurrencyLevel == StrictConcurrency::Complete)) {
SourceLoc loc = stmt->getUnsafeLoc();
bool implicit = stmt->getUnsafeLoc().isInvalid();
if (loc.isInvalid())
loc = stmt->getForLoc();
nextCall = new (ctx) UnsafeExpr(loc, nextCall, Type(), implicit);
}
// The iterator type must conform to IteratorProtocol.
{
ProtocolDecl *iteratorProto = TypeChecker::getProtocol(
cs.getASTContext(), stmt->getForLoc(),
isAsync ? KnownProtocolKind::AsyncIteratorProtocol
: KnownProtocolKind::IteratorProtocol);
if (!iteratorProto)
return std::nullopt;
ContextualTypeInfo contextInfo(iteratorProto->getDeclaredInterfaceType(),
CTP_ForEachSequence);
cs.setContextualInfo(nextRef->getBase(), contextInfo);
}
SyntacticElementTarget nextTarget(nextCall, dc, CTP_Unused,
/*contextualType=*/Type(),
/*isDiscarded=*/false);
if (cs.generateConstraints(nextTarget, FreeTypeVariableBinding::Disallow))
return std::nullopt;
sequenceIterationInfo.nextCall = nextTarget.getAsExpr();
cs.setTargetFor(sequenceIterationInfo.nextCall, nextTarget);
}
// Generate constraints for the pattern.
Type initType =
cs.generateConstraints(typeCheckedPattern, elementLocator,
shouldBindPatternVarsOneWay, nullptr, 0);
if (!initType)
return std::nullopt;
// Add a conversion constraint between the element type of the sequence
// and the type of the element pattern.
auto *elementTypeLoc = cs.getConstraintLocator(
elementLocator, ConstraintLocator::OptionalInjection);
auto elementType = cs.createTypeVariable(elementTypeLoc,
/*flags=*/0);
{
auto nextType = cs.getType(sequenceIterationInfo.nextCall);
cs.addConstraint(ConstraintKind::OptionalObject, nextType, elementType,
elementTypeLoc);
cs.addConstraint(ConstraintKind::Conversion, elementType, initType,
elementLocator);
}
// Populate all of the information for a for-each loop.
sequenceIterationInfo.elementType = elementType;
sequenceIterationInfo.initType = initType;
return sequenceIterationInfo;
}
static std::optional<SyntacticElementTarget>
generateForEachPreambleConstraints(ConstraintSystem &cs,
SyntacticElementTarget target) {
ForEachStmt *stmt = target.getAsForEachStmt();
auto *forEachExpr = stmt->getParsedSequence();
auto *dc = target.getDeclContext();
auto elementLocator = cs.getConstraintLocator(
forEachExpr, ConstraintLocator::SequenceElementType);
Pattern *pattern = TypeChecker::resolvePattern(stmt->getPattern(), dc,
/*isStmtCondition*/ false);
if (!pattern)
return std::nullopt;
target.setPattern(pattern);
auto contextualPattern = ContextualPattern::forRawPattern(pattern, dc);
if (TypeChecker::typeCheckPattern(contextualPattern)->hasError()) {
return std::nullopt;
}
if (isa<PackExpansionExpr>(forEachExpr)) {
auto *expansion = cast<PackExpansionExpr>(forEachExpr);
// Generate constraints for the pattern.
Type patternType = cs.generateConstraints(
pattern, elementLocator, target.shouldBindPatternVarsOneWay(), nullptr,
0);
if (!patternType)
return std::nullopt;
if (auto whereClause = stmt->getWhere()) {
cs.recordFix(IgnoreWhereClauseInPackIteration::create(
cs, cs.getConstraintLocator(whereClause)));
}
auto packIterationInfo =
generateForEachStmtConstraints(cs, dc, expansion, patternType);
if (!packIterationInfo) {
return std::nullopt;
}
target.getForEachStmtInfo() = *packIterationInfo;
} else {
auto sequenceIterationInfo = generateForEachStmtConstraints(
cs, dc, stmt, pattern, target.shouldBindPatternVarsOneWay());
if (!sequenceIterationInfo) {
return std::nullopt;
}
target.getForEachStmtInfo() = *sequenceIterationInfo;
}
return target;
}
bool ConstraintSystem::generateConstraints(
SyntacticElementTarget &target,
FreeTypeVariableBinding allowFreeTypeVariables) {
if (Expr *expr = target.getAsExpr()) {
// If the target requires an optional of some type, form a new appropriate
// type variable and update the target's type with an optional of that
// type variable.
if (target.isOptionalSomePatternInit()) {
assert(!target.getExprContextualType() &&
"some pattern cannot have contextual type pre-configured");
auto *convertTypeLocator = getConstraintLocator(
expr, LocatorPathElt::ContextualType(
target.getExprContextualTypePurpose()));
Type var = createTypeVariable(convertTypeLocator, TVO_CanBindToNoEscape);
target.setExprConversionType(TypeChecker::getOptionalType(expr->getLoc(), var));
}
// If we have a parent return statement, record whether it's implied.
if (auto *RS = target.getParentReturnStmt()) {
if (RS->isImplied())
recordImpliedResult(expr, ImpliedResultKind::Regular);
}
expr = buildTypeErasedExpr(expr, target.getDeclContext(),
target.getExprContextualType(),
target.getExprContextualTypePurpose());
// Generate constraints for the main system.
expr = generateConstraints(expr, target.getDeclContext());
if (!expr)
return true;
target.setExpr(expr);
// If there is a type that we're expected to convert to, add the conversion
// constraint.
if (Type convertType = target.getExprConversionType()) {
ContextualTypePurpose ctp = target.getExprContextualTypePurpose();
// If a custom locator wasn't specified, create a locator anchored on
// the expression itself.
auto *convertTypeLocator = target.getExprConvertTypeLocator();
if (!convertTypeLocator) {
convertTypeLocator =
getConstraintLocator(expr, LocatorPathElt::ContextualType(ctp));
}
auto getLocator = [&](Type ty) -> ConstraintLocator * {
// If we have a placeholder originating from a PlaceholderTypeRepr,
// tack that on to the locator.
if (auto *placeholderTy = ty->getAs<PlaceholderType>())
if (auto *typeRepr = placeholderTy->getOriginator()
.dyn_cast<TypeRepr *>())
return getConstraintLocator(
convertTypeLocator,
LocatorPathElt::PlaceholderType(typeRepr));
return convertTypeLocator;
};
// Substitute type variables in for placeholder types (and unresolved
// types, if allowed).
if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) {
convertType = convertType.transformRec([&](Type type) -> std::optional<Type> {
if (type->is<UnresolvedType>() || type->is<PlaceholderType>()) {
return Type(createTypeVariable(getLocator(type),
TVO_CanBindToNoEscape |
TVO_PrefersSubtypeBinding |
TVO_CanBindToHole));
}
return std::nullopt;
});
} else {
convertType = convertType.transformRec([&](Type type) -> std::optional<Type> {
if (type->is<PlaceholderType>()) {
return Type(createTypeVariable(getLocator(type),
TVO_CanBindToNoEscape |
TVO_PrefersSubtypeBinding |
TVO_CanBindToHole));
}
return std::nullopt;
});
}
addContextualConversionConstraint(expr, convertType, ctp,
convertTypeLocator);
}
// For an initialization target, generate constraints for the pattern.
if (target.isForInitialization() &&
generateInitPatternConstraints(*this, target, expr)) {
return true;
}
if (isDebugMode()) {
auto &log = llvm::errs();
log << "\n---Initial constraints for the given expression---\n";
print(log, expr);
log << "\n";
print(log);
log << "\n";
}
return false;
}
switch (target.kind) {
case SyntacticElementTarget::Kind::expression:
llvm_unreachable("Handled above");
case SyntacticElementTarget::Kind::closure:
case SyntacticElementTarget::Kind::caseLabelItem:
case SyntacticElementTarget::Kind::function:
case SyntacticElementTarget::Kind::stmtCondition:
llvm_unreachable("Handled separately");
case SyntacticElementTarget::Kind::patternBinding: {
auto patternBinding = target.getAsPatternBinding();
auto dc = target.getDeclContext();
bool hadError = false;
/// Generate constraints for each pattern binding entry
for (unsigned index : range(patternBinding->getNumPatternEntries())) {
auto *pattern = TypeChecker::resolvePattern(
patternBinding->getPattern(index), dc, /*isStmtCondition=*/true);
if (!pattern)
return true;
// Reset binding to point to the resolved pattern. This is required
// before calling `forPatternBindingDecl`.
patternBinding->setPattern(index, pattern);
auto contextualPattern =
ContextualPattern::forPatternBindingDecl(patternBinding, index);
Type patternType = TypeChecker::typeCheckPattern(contextualPattern);
// Fail early if pattern couldn't be type-checked.
if (!patternType || patternType->hasError())
return true;
auto *init = patternBinding->getInit(index);
if (!init && patternBinding->isDefaultInitializable(index) &&
pattern->hasStorage()) {
init = TypeChecker::buildDefaultInitializer(patternType);
}
auto target = init ? SyntacticElementTarget::forInitialization(
init, patternType, patternBinding, index,
/*bindPatternVarsOneWay=*/true)
: SyntacticElementTarget::forUninitializedVar(
patternBinding, index, patternType);
if (generateConstraints(target)) {
hadError = true;
continue;
}
// Keep track of this binding entry.
setTargetFor({patternBinding, index}, target);
}
return hadError;
}
case SyntacticElementTarget::Kind::uninitializedVar: {
if (auto *wrappedVar = target.getAsUninitializedWrappedVar()) {
auto propertyType = wrappedVar->getTypeInContext();
if (propertyType->hasError())
return true;
return generateWrappedPropertyTypeConstraints(
wrappedVar, /*initializerType=*/Type(), propertyType);
} else {
auto pattern = target.getAsUninitializedVar();
auto locator = getConstraintLocator(
pattern, LocatorPathElt::ContextualType(CTP_Initialization));
// Generate constraints to bind all of the internal declarations
// and verify the pattern.
Type patternType = generateConstraints(
pattern, locator, /*shouldBindPatternVarsOneWay*/ true,
target.getPatternBindingOfUninitializedVar(),
target.getIndexOfUninitializedVar());
return !patternType;
}
}
case SyntacticElementTarget::Kind::forEachPreamble: {
// For a for-each statement, generate constraints for the pattern, where
// clause, and sequence traversal.
auto resultTarget = generateForEachPreambleConstraints(*this, target);
if (!resultTarget)
return true;
target = *resultTarget;
return false;
}
}
}
Expr *ConstraintSystem::generateConstraints(Expr *expr, DeclContext *dc) {
InputExprs.insert(expr);
return generateConstraintsFor(*this, expr, dc);
}
Type ConstraintSystem::generateConstraints(
Pattern *pattern, ConstraintLocatorBuilder locator,
bool bindPatternVarsOneWay, PatternBindingDecl *patternBinding,
unsigned patternIndex) {
ConstraintGenerator cg(*this, nullptr);
auto ty = cg.getTypeForPattern(pattern, locator, bindPatternVarsOneWay,
patternBinding, patternIndex);
assert(ty);
// Gather the ExprPatterns, and form a conjunction for their expressions.
SmallVector<ExprPattern *, 4> exprPatterns;
pattern->forEachNode([&](Pattern *P) {
if (auto *EP = dyn_cast<ExprPattern>(P))
exprPatterns.push_back(EP);
});
if (!exprPatterns.empty())
generateConstraints(exprPatterns, getConstraintLocator(pattern));
return ty;
}
bool ConstraintSystem::generateConstraints(StmtCondition condition,
DeclContext *dc) {
// FIXME: This should be folded into constraint generation for conditions.
auto boolDecl = getASTContext().getBoolDecl();
if (!boolDecl) {
return true;
}
Type boolTy = boolDecl->getDeclaredInterfaceType();
for (auto &condElement : condition) {
switch (condElement.getKind()) {
case StmtConditionElement::CK_Availability:
// Nothing to do here.
continue;
case StmtConditionElement::CK_HasSymbol: {
Expr *symbolExpr = condElement.getHasSymbolInfo()->getSymbolExpr();
auto target = SyntacticElementTarget(symbolExpr, dc, CTP_Unused, Type(),
/*isDiscarded=*/false);
if (generateConstraints(target))
return true;
setTargetFor(&condElement, target);
continue;
}
case StmtConditionElement::CK_Boolean: {
Expr *condExpr = condElement.getBoolean();
auto target = SyntacticElementTarget(condExpr, dc, CTP_Condition, boolTy,
/*isDiscarded=*/false);
if (generateConstraints(target, FreeTypeVariableBinding::Disallow))
return true;
setTargetFor(&condElement, target);
continue;
}
case StmtConditionElement::CK_PatternBinding: {
auto *pattern = TypeChecker::resolvePattern(
condElement.getPattern(), dc, /*isStmtCondition*/true);
if (!pattern)
return true;
auto target = SyntacticElementTarget::forInitialization(
condElement.getInitializer(), dc, Type(), pattern,
/*bindPatternVarsOneWay=*/true);
if (generateConstraints(target, FreeTypeVariableBinding::Disallow))
return true;
setTargetFor(&condElement, target);
continue;
}
}
}
return false;
}
void ConstraintSystem::applyPropertyWrapper(
Expr *anchor, AppliedPropertyWrapper applied) {
appliedPropertyWrappers[anchor].push_back(applied);
if (solverState)
recordChange(SolverTrail::Change::AppliedPropertyWrapper(anchor));
}
void ConstraintSystem::removePropertyWrapper(Expr *anchor) {
auto found = appliedPropertyWrappers.find(anchor);
ASSERT(found != appliedPropertyWrappers.end());
auto &wrappers = found->second;
ASSERT(!wrappers.empty());
wrappers.pop_back();
if (wrappers.empty()) {
bool erased = appliedPropertyWrappers.erase(anchor);
ASSERT(erased);
}
}
ConstraintSystem::TypeMatchResult
ConstraintSystem::applyPropertyWrapperToParameter(
Type wrapperType, Type paramType, ParamDecl *param, Identifier argLabel,
ConstraintKind matchKind, ConstraintLocator *locator,
ConstraintLocator *calleeLocator) {
Expr *anchor = getAsExpr(calleeLocator->getAnchor());
auto recordPropertyWrapperFix = [&](ConstraintFix *fix) -> TypeMatchResult {
if (!shouldAttemptFixes())
return getTypeMatchFailure(locator);
recordAnyTypeVarAsPotentialHole(paramType);
if (recordFix(fix))
return getTypeMatchFailure(locator);
return getTypeMatchSuccess();
};
// Incorrect use of projected value argument
if (argLabel.hasDollarPrefix() &&
(!param || !param->hasExternalPropertyWrapper())) {
auto *loc = getConstraintLocator(locator);
auto *fix =
RemoveProjectedValueArgument::create(*this, wrapperType, param, loc);
return recordPropertyWrapperFix(fix);
}
// Missing wrapped value initializer
auto wrapperInfo = param->getAttachedPropertyWrapperTypeInfo(0);
if (!argLabel.hasDollarPrefix() && !wrapperInfo.wrappedValueInit &&
param->hasExternalPropertyWrapper()) {
auto *loc = getConstraintLocator(locator);
auto *fix = UsePropertyWrapper::create(
*this, param, /*usingProjection=*/true, paramType, wrapperType, loc);
return recordPropertyWrapperFix(fix);
}
if (argLabel.hasDollarPrefix()) {
Type projectionType = computeProjectedValueType(param, wrapperType);
addConstraint(matchKind, paramType, projectionType, locator);
if (param->hasImplicitPropertyWrapper()) {
auto wrappedValueType = getType(param->getPropertyWrapperWrappedValueVar());
addConstraint(ConstraintKind::PropertyWrapper, projectionType, wrappedValueType,
getConstraintLocator(param));
setType(param->getPropertyWrapperProjectionVar(), projectionType);
}
applyPropertyWrapper(anchor, { wrapperType, PropertyWrapperInitKind::ProjectedValue });
} else if (param->hasExternalPropertyWrapper()) {
Type wrappedValueType = computeWrappedValueType(param, wrapperType);
addConstraint(matchKind, paramType, wrappedValueType, locator);
setType(param->getPropertyWrapperWrappedValueVar(), wrappedValueType);
applyPropertyWrapper(anchor, { wrapperType, PropertyWrapperInitKind::WrappedValue });
} else {
return getTypeMatchFailure(locator);
}
return getTypeMatchSuccess();
}
void ConstraintSystem::optimizeConstraints(Expr *e) {
if (getASTContext().TypeCheckerOpts.DisableConstraintSolverPerformanceHacks)
return;
SmallVector<Expr *, 16> linkedExprs;
// Collect any linked expressions.
LinkedExprCollector collector(linkedExprs);
e->walk(collector);
// Favor types, as appropriate.
for (auto linkedExpr : linkedExprs) {
computeFavoredTypeForExpr(linkedExpr, *this);
}
// Optimize the constraints.
ConstraintOptimizer optimizer(*this);
e->walk(optimizer);
}
struct ResolvedMemberResult::Implementation {
llvm::SmallVector<ValueDecl*, 4> AllDecls;
unsigned ViableStartIdx;
std::optional<unsigned> BestIdx;
};
ResolvedMemberResult::ResolvedMemberResult(): Impl(new Implementation()) {}
ResolvedMemberResult::~ResolvedMemberResult() { delete Impl; }
ResolvedMemberResult::operator bool() const {
return !Impl->AllDecls.empty();
}
bool ResolvedMemberResult::
hasBestOverload() const { return Impl->BestIdx.has_value(); }
ValueDecl* ResolvedMemberResult::
getBestOverload() const { return Impl->AllDecls[Impl->BestIdx.value()]; }
ArrayRef<ValueDecl*> ResolvedMemberResult::
getMemberDecls(InterestedMemberKind Kind) {
auto Result = llvm::ArrayRef(Impl->AllDecls);
switch (Kind) {
case InterestedMemberKind::Viable:
return Result.slice(Impl->ViableStartIdx);
case InterestedMemberKind::Unviable:
return Result.slice(0, Impl->ViableStartIdx);
case InterestedMemberKind::All:
return Result;
}
llvm_unreachable("unhandled kind");
}
ResolvedMemberResult swift::resolveValueMember(DeclContext &DC, Type BaseTy,
DeclName Name) {
ResolvedMemberResult Result;
ConstraintSystem CS(&DC, std::nullopt);
// Look up all members of BaseTy with the given Name.
MemberLookupResult LookupResult =
CS.performMemberLookup(ConstraintKind::ValueMember, DeclNameRef(Name),
BaseTy, FunctionRefInfo::singleBaseNameApply(),
CS.getConstraintLocator({}), false);
// Keep track of all the unviable members.
for (auto Can : LookupResult.UnviableCandidates)
Result.Impl->AllDecls.push_back(Can.getDecl());
// Keep track of the start of viable choices.
Result.Impl->ViableStartIdx = Result.Impl->AllDecls.size();
// If no viable members, we are done.
if (LookupResult.ViableCandidates.empty())
return Result;
// If there's only one viable member, that is the best one.
if (LookupResult.ViableCandidates.size() == 1) {
Result.Impl->BestIdx = Result.Impl->AllDecls.size();
Result.Impl->AllDecls.push_back(LookupResult.ViableCandidates[0].getDecl());
return Result;
}
// Try to figure out the best overload.
ConstraintLocator *Locator = CS.getConstraintLocator({});
TypeVariableType *TV = CS.createTypeVariable(Locator,
TVO_CanBindToLValue |
TVO_CanBindToNoEscape);
CS.addOverloadSet(TV, LookupResult.ViableCandidates, &DC, Locator);
std::optional<Solution> OpSolution = CS.solveSingle();
ValueDecl *Selected = nullptr;
if (OpSolution.has_value()) {
Selected = OpSolution.value().overloadChoices[Locator].choice.getDecl();
}
for (OverloadChoice& Choice : LookupResult.ViableCandidates) {
ValueDecl *VD = Choice.getDecl();
// If this VD is the best overload, keep track of its index.
if (VD == Selected)
Result.Impl->BestIdx = Result.Impl->AllDecls.size();
Result.Impl->AllDecls.push_back(VD);
}
return Result;
}