mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Generic methods declared in protocols (and extensions thereof) cannot be used on existential values, because there is no way to specialize them for all potential types. Diagnose such cases in Embedded Swift mode and via `-Wwarning EmbeddedRestrictions`. This adds a bunch more warnings to the standard library that we'll need to clean up, probably by `#if`'ing more code out. Part of rdar://119383905.
10177 lines
390 KiB
C++
10177 lines
390 KiB
C++
//===--- CSApply.cpp - Constraint Application -----------------------------===//
|
|
//
|
|
// 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 application of a solution to a constraint
|
|
// system to a particular expression, resulting in a
|
|
// fully-type-checked expression.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CSDiagnostics.h"
|
|
#include "CodeSynthesis.h"
|
|
#include "MiscDiagnostics.h"
|
|
#include "OpenedExistentials.h"
|
|
#include "TypeCheckConcurrency.h"
|
|
#include "TypeCheckEmbedded.h"
|
|
#include "TypeCheckMacros.h"
|
|
#include "TypeCheckProtocol.h"
|
|
#include "TypeCheckType.h"
|
|
#include "swift/AST/ASTVisitor.h"
|
|
#include "swift/AST/ASTWalker.h"
|
|
#include "swift/AST/ClangModuleLoader.h"
|
|
#include "swift/AST/ConformanceLookup.h"
|
|
#include "swift/AST/Effects.h"
|
|
#include "swift/AST/ExistentialLayout.h"
|
|
#include "swift/AST/GenericEnvironment.h"
|
|
#include "swift/AST/GenericSignature.h"
|
|
#include "swift/AST/Initializer.h"
|
|
#include "swift/AST/OperatorNameLookup.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/ProtocolConformance.h"
|
|
#include "swift/AST/SourceFile.h"
|
|
#include "swift/AST/SubstitutionMap.h"
|
|
#include "swift/AST/TypeCheckRequests.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/Basic/Defer.h"
|
|
#include "swift/Basic/StringExtras.h"
|
|
#include "swift/Sema/ConstraintSystem.h"
|
|
#include "swift/Sema/SolutionResult.h"
|
|
#include "clang/AST/DeclTemplate.h"
|
|
#include "clang/AST/Mangle.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/Sema/Sema.h"
|
|
#include "clang/Sema/Template.h"
|
|
#include "clang/Sema/TemplateDeduction.h"
|
|
#include "llvm/ADT/APFloat.h"
|
|
#include "llvm/ADT/APInt.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/SaveAndRestore.h"
|
|
|
|
using namespace swift;
|
|
using namespace constraints;
|
|
|
|
static bool isClosureLiteralExpr(Expr *expr) {
|
|
expr = expr->getSemanticsProvidingExpr();
|
|
return (isa<CaptureListExpr>(expr) || isa<ClosureExpr>(expr));
|
|
}
|
|
|
|
bool Solution::hasFixedType(TypeVariableType *typeVar) const {
|
|
auto knownBinding = typeBindings.find(typeVar);
|
|
return knownBinding != typeBindings.end();
|
|
}
|
|
|
|
/// Retrieve the fixed type for the given type variable.
|
|
Type Solution::getFixedType(TypeVariableType *typeVar) const {
|
|
auto knownBinding = typeBindings.find(typeVar);
|
|
assert(knownBinding != typeBindings.end());
|
|
return knownBinding->second;
|
|
}
|
|
|
|
/// Determine whether the given type is an opened AnyObject.
|
|
///
|
|
/// This comes up in computeSubstitutions() when accessing
|
|
/// members via dynamic lookup.
|
|
static bool isOpenedAnyObject(Type type) {
|
|
auto archetype = type->getAs<ExistentialArchetypeType>();
|
|
if (!archetype || !archetype->isRoot())
|
|
return false;
|
|
|
|
return archetype->getExistentialType()->isAnyObject();
|
|
}
|
|
|
|
SubstitutionMap
|
|
Solution::computeSubstitutions(NullablePtr<ValueDecl> decl,
|
|
GenericSignature sig,
|
|
ConstraintLocator *locator) const {
|
|
if (sig.isNull())
|
|
return SubstitutionMap();
|
|
|
|
// Gather the substitutions from dependent types to concrete types.
|
|
auto openedTypes = OpenedTypes.find(locator);
|
|
|
|
// If we have a member reference on an existential, there are no
|
|
// opened types or substitutions.
|
|
if (openedTypes == OpenedTypes.end())
|
|
return SubstitutionMap();
|
|
|
|
SmallVector<Type, 4> replacementTypes;
|
|
for (const auto &opened : openedTypes->second) {
|
|
auto type = getFixedType(opened.second);
|
|
if (opened.first->isParameterPack()) {
|
|
if (type->is<PlaceholderType>()) {
|
|
auto &ctx = type->getASTContext();
|
|
type =
|
|
PackType::get(ctx, {PackExpansionType::get(ctx.TheUnresolvedType,
|
|
ctx.TheUnresolvedType)});
|
|
} else if (!type->is<PackType>())
|
|
type = PackType::getSingletonPackExpansion(type);
|
|
}
|
|
replacementTypes.push_back(simplifyType(type));
|
|
}
|
|
|
|
auto lookupConformanceFn =
|
|
[&](InFlightSubstitution &IFS, Type original,
|
|
ProtocolDecl *protoType) -> ProtocolConformanceRef {
|
|
auto replacement = original.subst(IFS);
|
|
assert(!replacement->is<GenericTypeParamType>());
|
|
|
|
if (replacement->hasError() ||
|
|
isOpenedAnyObject(replacement) ||
|
|
replacement->is<GenericTypeParamType>()) {
|
|
return ProtocolConformanceRef::forAbstract(replacement, protoType);
|
|
}
|
|
|
|
// FIXME: Retrieve the conformance from the solution itself.
|
|
auto conformance = lookupConformance(
|
|
replacement, protoType, /*allowMissing=*/true);
|
|
|
|
if (conformance.isInvalid()) {
|
|
auto synthesized = SynthesizedConformances.find(locator);
|
|
if (synthesized != SynthesizedConformances.end()) {
|
|
return ProtocolConformanceRef::forAbstract(
|
|
replacement, synthesized->second);
|
|
}
|
|
}
|
|
|
|
return conformance;
|
|
};
|
|
|
|
auto subs = SubstitutionMap::get(sig, replacementTypes, lookupConformanceFn);
|
|
ASSERT(!subs.getRecursiveProperties().isSolverAllocated());
|
|
return subs;
|
|
}
|
|
|
|
// Lazily instantiate function definitions for class template specializations.
|
|
// Members of a class template specialization will be instantiated here (not
|
|
// when imported). If this method has already be instantiated, then this is a
|
|
// no-op.
|
|
static void maybeInstantiateCXXMethodDefinition(ValueDecl *decl) {
|
|
if (const auto *constMethod =
|
|
dyn_cast_or_null<clang::CXXMethodDecl>(decl->getClangDecl())) {
|
|
auto method = const_cast<clang::CXXMethodDecl *>(constMethod);
|
|
// Make sure that this method is part of a class template specialization.
|
|
if (method->getTemplateInstantiationPattern())
|
|
decl->getASTContext()
|
|
.getClangModuleLoader()
|
|
->getClangSema()
|
|
.InstantiateFunctionDefinition(method->getLocation(), method);
|
|
}
|
|
}
|
|
|
|
ConcreteDeclRef
|
|
Solution::resolveConcreteDeclRef(ValueDecl *decl,
|
|
ConstraintLocator *locator) const {
|
|
if (!decl)
|
|
return ConcreteDeclRef();
|
|
|
|
// Get the generic signature of the decl and compute the substitutions.
|
|
auto sig = decl->getInnermostDeclContext()->getGenericSignatureOfContext();
|
|
auto subst = computeSubstitutions(decl, sig, locator);
|
|
|
|
maybeInstantiateCXXMethodDefinition(decl);
|
|
|
|
// If this is a C++ function template, get it's specialization for the given
|
|
// substitution map and update the decl accordingly.
|
|
if (isa_and_nonnull<clang::FunctionTemplateDecl>(decl->getClangDecl())) {
|
|
auto moduleLoader = decl->getASTContext().getClangModuleLoader();
|
|
return moduleLoader->getCXXFunctionTemplateSpecialization(subst, decl);
|
|
}
|
|
|
|
return ConcreteDeclRef(decl, subst);
|
|
}
|
|
|
|
SelectedOverload
|
|
Solution::getCalleeOverloadChoice(ConstraintLocator *locator) const {
|
|
return getOverloadChoice(getCalleeLocator(locator));
|
|
}
|
|
|
|
std::optional<SelectedOverload>
|
|
Solution::getCalleeOverloadChoiceIfAvailable(ConstraintLocator *locator) const {
|
|
return getOverloadChoiceIfAvailable(getCalleeLocator(locator));
|
|
}
|
|
|
|
ConstraintLocator *Solution::getCalleeLocator(ConstraintLocator *locator,
|
|
bool lookThroughApply) const {
|
|
auto &cs = getConstraintSystem();
|
|
return cs.getCalleeLocator(
|
|
locator, lookThroughApply,
|
|
[&](Expr *expr) -> Type { return getType(expr); },
|
|
[&](Type type) -> Type { return simplifyType(type)->getRValueType(); },
|
|
[&](ConstraintLocator *locator) -> std::optional<SelectedOverload> {
|
|
return getOverloadChoiceIfAvailable(locator);
|
|
});
|
|
}
|
|
|
|
ConstraintLocator *
|
|
Solution::getConstraintLocator(ASTNode anchor,
|
|
ArrayRef<LocatorPathElt> path) const {
|
|
auto &cs = getConstraintSystem();
|
|
return cs.getConstraintLocator(anchor, path);
|
|
}
|
|
|
|
ConstraintLocator *
|
|
Solution::getConstraintLocator(ConstraintLocator *base,
|
|
ArrayRef<LocatorPathElt> path) const {
|
|
auto &cs = getConstraintSystem();
|
|
return cs.getConstraintLocator(base, path);
|
|
}
|
|
|
|
/// Return the implicit access kind for a MemberRefExpr with the
|
|
/// specified base and member in the specified DeclContext.
|
|
static AccessSemantics
|
|
getImplicitMemberReferenceAccessSemantics(Expr *base, VarDecl *member,
|
|
DeclContext *DC) {
|
|
// Check whether this is a member access on 'self'.
|
|
bool isAccessOnSelf = false;
|
|
if (auto *baseDRE = dyn_cast<DeclRefExpr>(base->getValueProvidingExpr()))
|
|
if (auto *baseVar = dyn_cast<VarDecl>(baseDRE->getDecl()))
|
|
isAccessOnSelf = baseVar->isSelfParameter();
|
|
|
|
// If the value is always directly accessed from this context, do it.
|
|
return member->getAccessSemanticsFromContext(DC, isAccessOnSelf);
|
|
}
|
|
|
|
/// This extends functionality of `Expr::isTypeReference` with
|
|
/// support for `UnresolvedDotExpr` and `UnresolvedMemberExpr`.
|
|
/// This method could be used on not yet fully type-checked AST.
|
|
bool ConstraintSystem::isTypeReference(Expr *E) {
|
|
return E->isTypeReference(
|
|
[&](Expr *E) -> Type { return simplifyType(getType(E)); },
|
|
[&](Expr *E) -> Decl * {
|
|
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(E)) {
|
|
return findResolvedMemberRef(
|
|
getConstraintLocator(UDE, ConstraintLocator::Member));
|
|
}
|
|
|
|
if (auto *UME = dyn_cast<UnresolvedMemberExpr>(E)) {
|
|
return findResolvedMemberRef(
|
|
getConstraintLocator(UME, ConstraintLocator::UnresolvedMember));
|
|
}
|
|
|
|
if (isa<OverloadSetRefExpr>(E))
|
|
return findResolvedMemberRef(
|
|
getConstraintLocator(const_cast<Expr *>(E)));
|
|
|
|
return nullptr;
|
|
});
|
|
}
|
|
|
|
bool Solution::isTypeReference(Expr *E) const {
|
|
return E->isTypeReference(
|
|
[&](Expr *expr) -> Type { return simplifyType(getType(expr)); },
|
|
[&](Expr *expr) -> Decl * {
|
|
ConstraintLocator *locator = nullptr;
|
|
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(E)) {
|
|
locator = getConstraintLocator(UDE, {ConstraintLocator::Member});
|
|
}
|
|
|
|
if (auto *UME = dyn_cast<UnresolvedMemberExpr>(E)) {
|
|
locator =
|
|
getConstraintLocator(UME, {ConstraintLocator::UnresolvedMember});
|
|
}
|
|
|
|
if (isa<OverloadSetRefExpr>(E))
|
|
locator = getConstraintLocator(const_cast<Expr *>(E));
|
|
|
|
if (locator) {
|
|
if (auto selectedOverload = getOverloadChoiceIfAvailable(locator)) {
|
|
const auto &choice = selectedOverload->choice;
|
|
return choice.getDeclOrNull();
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
});
|
|
}
|
|
|
|
bool ConstraintSystem::isStaticallyDerivedMetatype(Expr *E) {
|
|
return E->isStaticallyDerivedMetatype(
|
|
[&](Expr *E) -> Type { return simplifyType(getType(E)); },
|
|
[&](Expr *E) -> bool { return isTypeReference(E); });
|
|
}
|
|
|
|
bool Solution::isStaticallyDerivedMetatype(Expr *E) const {
|
|
return E->isStaticallyDerivedMetatype(
|
|
[&](Expr *E) -> Type { return simplifyType(getType(E)); },
|
|
[&](Expr *E) -> bool { return isTypeReference(E); });
|
|
}
|
|
|
|
Type ConstraintSystem::getInstanceType(TypeExpr *E) {
|
|
if (!hasType(E))
|
|
return Type();
|
|
|
|
if (auto metaType = getType(E)->getAs<MetatypeType>())
|
|
return metaType->getInstanceType();
|
|
|
|
return ErrorType::get(getType(E)->getASTContext());
|
|
}
|
|
|
|
Type ConstraintSystem::getResultType(const AbstractClosureExpr *E) {
|
|
return E->getResultType([&](Expr *E) -> Type { return getType(E); });
|
|
}
|
|
|
|
static bool buildObjCKeyPathString(KeyPathExpr *E,
|
|
llvm::SmallVectorImpl<char> &buf) {
|
|
for (auto &component : E->getComponents()) {
|
|
switch (component.getKind()) {
|
|
case KeyPathExpr::Component::Kind::OptionalChain:
|
|
case KeyPathExpr::Component::Kind::OptionalForce:
|
|
case KeyPathExpr::Component::Kind::OptionalWrap:
|
|
// KVC propagates nulls, so these don't affect the key path string.
|
|
continue;
|
|
case KeyPathExpr::Component::Kind::Identity:
|
|
// The identity component can be elided from the KVC string (unless it's
|
|
// the only component, in which case we use @"self").
|
|
continue;
|
|
|
|
case KeyPathExpr::Component::Kind::Member: {
|
|
// Property references must be to @objc properties.
|
|
// TODO: If we added special properties matching KVC operators like
|
|
// '@sum', '@count', etc. those could be mapped too.
|
|
if (auto property = dyn_cast<VarDecl>(component.getDeclRef().getDecl())) {
|
|
if (!property->isObjC())
|
|
return false;
|
|
if (!buf.empty()) {
|
|
buf.push_back('.');
|
|
}
|
|
auto objcName = property->getObjCPropertyName().str();
|
|
buf.append(objcName.begin(), objcName.end());
|
|
}
|
|
continue;
|
|
}
|
|
|
|
case KeyPathExpr::Component::Kind::Apply:
|
|
case KeyPathExpr::Component::Kind::TupleElement:
|
|
case KeyPathExpr::Component::Kind::Subscript:
|
|
// Subscripts, methods and tuples aren't generally represented in KVC.
|
|
// TODO: There are some subscript forms we could map to KVC, such as
|
|
// when indexing a Dictionary or NSDictionary by string, or when applying
|
|
// a mapping subscript operation to Array/Set or NSArray/NSSet.
|
|
return false;
|
|
case KeyPathExpr::Component::Kind::Invalid:
|
|
case KeyPathExpr::Component::Kind::UnresolvedMember:
|
|
case KeyPathExpr::Component::Kind::UnresolvedSubscript:
|
|
case KeyPathExpr::Component::Kind::UnresolvedApply:
|
|
case KeyPathExpr::Component::Kind::CodeCompletion:
|
|
// Don't bother building the key path string if the key path didn't even
|
|
// resolve.
|
|
return false;
|
|
case KeyPathExpr::Component::Kind::DictionaryKey:
|
|
llvm_unreachable("DictionaryKey only valid in #keyPath expressions.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// If there are no non-identity components, this is the "self" key.
|
|
if (buf.empty()) {
|
|
auto self = StringRef("self");
|
|
buf.append(self.begin(), self.end());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Since a cast to an optional will consume a noncopyable type, check to see
|
|
/// if injecting the value into an optional here will potentially be confusing.
|
|
static bool willHaveConfusingConsumption(Type type,
|
|
ConstraintLocatorBuilder locator,
|
|
ConstraintSystem &cs) {
|
|
assert(type);
|
|
if (type->isCopyable())
|
|
return false; /// If it's a copyable type, there's no confusion.
|
|
|
|
auto loc = cs.getConstraintLocator(locator);
|
|
if (!loc)
|
|
return true;
|
|
|
|
auto path = loc->getPath();
|
|
if (path.empty())
|
|
return true;
|
|
|
|
switch (loc->getPath().back().getKind()) {
|
|
case ConstraintLocator::FunctionResult:
|
|
case ConstraintLocator::ClosureResult:
|
|
case ConstraintLocator::ClosureBody:
|
|
case ConstraintLocator::ContextualType:
|
|
case ConstraintLocator::CoercionOperand:
|
|
return false; // These last-uses won't be confused for borrowing.
|
|
|
|
case ConstraintLocator::ApplyArgToParam: {
|
|
auto argLoc = loc->castLastElementTo<LocatorPathElt::ApplyArgToParam>();
|
|
auto paramFlags = argLoc.getParameterFlags();
|
|
// If the param declares borrowing, then this implicit consumption
|
|
// due to the conversion to pass the argument is indeed confusing.
|
|
if (paramFlags.getOwnershipSpecifier() == ParamSpecifier::Borrowing)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// Rewrites an expression by applying the solution of a constraint
|
|
/// system to that expression.
|
|
class ExprRewriter : public ExprVisitor<ExprRewriter, Expr *> {
|
|
// Delayed items to type-check.
|
|
SmallVector<Decl *, 4> LocalDeclsToTypeCheck;
|
|
SmallVector<MacroExpansionExpr *, 4> MacrosToExpand;
|
|
|
|
public:
|
|
ASTContext &ctx;
|
|
ConstraintSystem &cs;
|
|
DeclContext *dc;
|
|
Solution &solution;
|
|
std::optional<SyntacticElementTarget> target;
|
|
bool SuppressDiagnostics;
|
|
|
|
ExprRewriter(ConstraintSystem &cs, Solution &solution,
|
|
std::optional<SyntacticElementTarget> target,
|
|
bool suppressDiagnostics)
|
|
: ctx(cs.getASTContext()), cs(cs),
|
|
dc(target ? target->getDeclContext() : cs.DC),
|
|
solution(solution), target(target),
|
|
SuppressDiagnostics(suppressDiagnostics) {}
|
|
|
|
ConstraintSystem &getConstraintSystem() const { return cs; }
|
|
|
|
void addLocalDeclToTypeCheck(Decl *D) {
|
|
// If we're doing code completion, avoid doing any further type-checking,
|
|
// that should instead be handled by TypeCheckASTNodeAtLocRequest.
|
|
if (ctx.SourceMgr.hasIDEInspectionTargetBuffer())
|
|
return;
|
|
|
|
LocalDeclsToTypeCheck.push_back(D);
|
|
}
|
|
|
|
void addMacroToExpand(MacroExpansionExpr *E) {
|
|
// If we're doing code completion, avoid doing any further type-checking,
|
|
// that should instead be handled by TypeCheckASTNodeAtLocRequest.
|
|
if (ctx.SourceMgr.hasIDEInspectionTargetBuffer())
|
|
return;
|
|
|
|
MacrosToExpand.push_back(E);
|
|
}
|
|
|
|
/// Coerce the given tuple to another tuple type.
|
|
///
|
|
/// \param expr The expression we're converting.
|
|
///
|
|
/// \param fromTuple The tuple type we're converting from, which is the same
|
|
/// as \c expr->getType().
|
|
///
|
|
/// \param toTuple The tuple type we're converting to.
|
|
///
|
|
/// \param locator Locator describing where this tuple conversion occurs.
|
|
///
|
|
/// \param sources The sources of each of the elements to be used in the
|
|
/// resulting tuple, as provided by \c computeTupleShuffle.
|
|
Expr *coerceTupleToTuple(Expr *expr, TupleType *fromTuple,
|
|
TupleType *toTuple,
|
|
ConstraintLocatorBuilder locator,
|
|
ArrayRef<unsigned> sources);
|
|
|
|
/// Coerce a subclass, class-constrained archetype, class-constrained
|
|
/// existential or to a superclass type.
|
|
///
|
|
/// Also supports metatypes of the above.
|
|
///
|
|
/// \param expr The expression to be coerced.
|
|
/// \param toType The type to which the expression will be coerced.
|
|
///
|
|
/// \return The coerced expression, whose type will be equivalent to
|
|
/// \c toType.
|
|
Expr *coerceSuperclass(Expr *expr, Type toType);
|
|
|
|
/// Coerce the given value to existential type.
|
|
///
|
|
/// The following conversions are supported:
|
|
/// - concrete to existential
|
|
/// - existential to existential
|
|
/// - concrete metatype to existential metatype
|
|
/// - existential metatype to existential metatype
|
|
///
|
|
/// \param expr The expression to be coerced.
|
|
/// \param toType The type to which the expression will be coerced.
|
|
/// \param locator Locator describing where this existential conversion occurs.
|
|
///
|
|
/// \return The coerced expression, whose type will be equivalent to
|
|
/// \c toType.
|
|
Expr *coerceExistential(Expr *expr, Type toType,
|
|
ConstraintLocatorBuilder locator);
|
|
|
|
/// Coerce an expression of (possibly unchecked) optional
|
|
/// type to have a different (possibly unchecked) optional type.
|
|
Expr *coerceOptionalToOptional(Expr *expr, Type toType,
|
|
ConstraintLocatorBuilder locator);
|
|
|
|
/// Peephole an array upcast.
|
|
void peepholeArrayUpcast(ArrayExpr *expr, Type toType, bool bridged,
|
|
Type elementType,
|
|
ConstraintLocatorBuilder locator);
|
|
|
|
/// Peephole a dictionary upcast.
|
|
void peepholeDictionaryUpcast(DictionaryExpr *expr, Type toType,
|
|
bool bridged, Type keyType,
|
|
Type valueType,
|
|
ConstraintLocatorBuilder locator);
|
|
|
|
/// Try to peephole the collection upcast, eliminating the need for
|
|
/// a separate collection-upcast expression.
|
|
///
|
|
/// \returns true if the peephole operation succeeded, in which case
|
|
/// \c expr already subsumes the upcast.
|
|
bool peepholeCollectionUpcast(Expr *expr, Type toType, bool bridged,
|
|
ConstraintLocatorBuilder locator);
|
|
|
|
/// Build a collection upcast expression.
|
|
///
|
|
/// \param bridged Whether this is a bridging conversion, meaning that the
|
|
/// element types themselves are bridged (vs. simply coerced).
|
|
Expr *buildCollectionUpcastExpr(Expr *expr, Type toType,
|
|
bool bridged,
|
|
ConstraintLocatorBuilder locator);
|
|
|
|
/// Build the expression that performs a bridging operation from the
|
|
/// given expression to the given \c toType.
|
|
Expr *buildObjCBridgeExpr(Expr *expr, Type toType,
|
|
ConstraintLocatorBuilder locator);
|
|
|
|
static Type getBaseType(AnyFunctionType *fnType,
|
|
bool wantsRValueInstanceType = true) {
|
|
auto params = fnType->getParams();
|
|
assert(params.size() == 1);
|
|
|
|
const auto &base = params.front();
|
|
if (wantsRValueInstanceType)
|
|
return base.getPlainType()->getMetatypeInstanceType();
|
|
|
|
return base.getOldType();
|
|
}
|
|
|
|
/// Check whether it is possible to have an ObjC key path string for the keypath expression
|
|
/// and set the key path string, if yes
|
|
void checkAndSetObjCKeyPathString(KeyPathExpr *keyPath) {
|
|
if (ctx.LangOpts.EnableObjCInterop) {
|
|
SmallString<64> compatStringBuf;
|
|
if (buildObjCKeyPathString(keyPath, compatStringBuf)) {
|
|
auto stringCopy = ctx.AllocateCopy<char>(compatStringBuf.begin(),
|
|
compatStringBuf.end());
|
|
auto stringExpr = new (ctx) StringLiteralExpr(
|
|
StringRef(stringCopy, compatStringBuf.size()),
|
|
SourceRange(),
|
|
/*implicit*/ true);
|
|
cs.setType(
|
|
stringExpr,
|
|
ctx.getStringType());
|
|
keyPath->setObjCStringLiteralExpr(stringExpr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns None if the AST does not contain enough information to recover
|
|
// substitutions; this is different from an Optional(SubstitutionMap()),
|
|
// indicating a valid call to a non-generic operator.
|
|
std::optional<SubstitutionMap> getOperatorSubstitutions(ValueDecl *witness,
|
|
Type refType) {
|
|
// We have to recover substitutions in this hacky way because
|
|
// the AST does not retain enough information to devirtualize
|
|
// calls like this.
|
|
auto witnessType = witness->getInterfaceType();
|
|
|
|
// Compute the substitutions.
|
|
auto *gft = witnessType->getAs<GenericFunctionType>();
|
|
if (gft == nullptr) {
|
|
if (refType->isEqual(witnessType))
|
|
return SubstitutionMap();
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto sig = gft->getGenericSignature();
|
|
auto *env = sig.getGenericEnvironment();
|
|
|
|
witnessType = FunctionType::get(gft->getParams(),
|
|
gft->getResult(),
|
|
gft->getExtInfo());
|
|
witnessType = env->mapTypeIntoContext(witnessType);
|
|
|
|
TypeSubstitutionMap subs;
|
|
auto substType = witnessType->substituteBindingsTo(
|
|
refType,
|
|
[&](ArchetypeType *origType, CanType substType) -> CanType {
|
|
if (auto gpType = dyn_cast<GenericTypeParamType>(
|
|
origType->getInterfaceType()->getCanonicalType()))
|
|
subs[gpType] = substType;
|
|
|
|
return substType;
|
|
});
|
|
|
|
// If substitution failed, it means that the protocol requirement type
|
|
// and the witness type did not match up. The only time that this
|
|
// should happen is when the witness is defined in a base class and
|
|
// the actual call uses a derived class. For example,
|
|
//
|
|
// protocol P { func +(lhs: Self, rhs: Self) }
|
|
// class Base : P { func +(lhs: Base, rhs: Base) {} }
|
|
// class Derived : Base {}
|
|
//
|
|
// If we enter this code path with two operands of type Derived,
|
|
// we know we're calling the protocol requirement P.+, with a
|
|
// substituted type of (Derived, Derived) -> (). But the type of
|
|
// the witness is (Base, Base) -> (). Just bail out and make a
|
|
// witness method call in this rare case; SIL mandatory optimizations
|
|
// will likely devirtualize it anyway.
|
|
if (!substType)
|
|
return std::nullopt;
|
|
|
|
return SubstitutionMap::get(sig,
|
|
QueryTypeSubstitutionMap{subs},
|
|
LookUpConformanceInModule());
|
|
}
|
|
|
|
/// Determine whether the given reference is to a method on
|
|
/// a remote distributed actor in the given context.
|
|
bool isDistributedThunk(ConcreteDeclRef ref, Expr *context);
|
|
|
|
public:
|
|
/// Build a reference to the given declaration.
|
|
Expr *buildDeclRef(SelectedOverload overload, DeclNameLoc loc,
|
|
ConstraintLocatorBuilder locator, bool implicit) {
|
|
auto choice = overload.choice;
|
|
assert(choice.getKind() != OverloadChoiceKind::DeclViaDynamic);
|
|
auto *decl = choice.getDecl();
|
|
Type fullType = simplifyType(overload.openedFullType);
|
|
Type adjustedFullType = simplifyType(overload.adjustedOpenedFullType);
|
|
|
|
// Determine the declaration selected for this overloaded reference.
|
|
|
|
auto semantics = decl->getAccessSemanticsFromContext(dc,
|
|
/*isAccessOnSelf*/false);
|
|
|
|
// If this is a member of a nominal type, build a reference to the
|
|
// member with an implied base type.
|
|
if (decl->getDeclContext()->isTypeContext() && isa<FuncDecl>(decl)) {
|
|
assert(cast<FuncDecl>(decl)->isOperator() && "Must be an operator");
|
|
|
|
auto baseTy = getBaseType(adjustedFullType->castTo<FunctionType>());
|
|
|
|
// Handle operator requirements found in protocols.
|
|
if (auto proto = dyn_cast<ProtocolDecl>(decl->getDeclContext())) {
|
|
bool isCurried = shouldBuildCurryThunk(choice, /*baseIsInstance=*/false);
|
|
|
|
// If we have a concrete conformance, build a call to the witness.
|
|
//
|
|
// FIXME: This is awful. We should be able to handle this as a call to
|
|
// the protocol requirement with Self == the concrete type, and SILGen
|
|
// (or later) can devirtualize as appropriate.
|
|
auto conformance = checkConformance(baseTy, proto);
|
|
if (conformance.isConcrete()) {
|
|
if (auto witness = conformance.getConcrete()->getWitnessDecl(decl)) {
|
|
bool isMemberOperator = witness->getDeclContext()->isTypeContext();
|
|
|
|
if (!isMemberOperator || !isCurried) {
|
|
// The fullType was computed by substituting the protocol
|
|
// requirement so it always has a (Self) -> ... curried
|
|
// application. Strip it off if the witness was a top-level
|
|
// function.
|
|
Type refType;
|
|
if (isMemberOperator)
|
|
refType = adjustedFullType;
|
|
else
|
|
refType = adjustedFullType->castTo<AnyFunctionType>()->getResult();
|
|
|
|
// Build the AST for the call to the witness.
|
|
auto subMap = getOperatorSubstitutions(witness, refType);
|
|
if (subMap) {
|
|
ConcreteDeclRef witnessRef(witness, *subMap);
|
|
auto declRefExpr = new (ctx) DeclRefExpr(witnessRef, loc,
|
|
/*Implicit=*/false);
|
|
declRefExpr->setFunctionRefInfo(choice.getFunctionRefInfo());
|
|
cs.setType(declRefExpr, refType);
|
|
|
|
Expr *refExpr;
|
|
if (isMemberOperator) {
|
|
// If the operator is a type member, add the implicit
|
|
// (Self) -> ... call.
|
|
Expr *base =
|
|
TypeExpr::createImplicitHack(loc.getBaseNameLoc(), baseTy,
|
|
ctx);
|
|
cs.setType(base, MetatypeType::get(baseTy));
|
|
|
|
refExpr =
|
|
DotSyntaxCallExpr::create(ctx, declRefExpr, SourceLoc(),
|
|
Argument::unlabeled(base));
|
|
auto refType = adjustedFullType->castTo<FunctionType>()->getResult();
|
|
cs.setType(refExpr, refType);
|
|
} else {
|
|
refExpr = declRefExpr;
|
|
}
|
|
|
|
return forceUnwrapIfExpected(refExpr, locator);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build a reference to the member.
|
|
Expr *base =
|
|
TypeExpr::createImplicitHack(loc.getBaseNameLoc(), baseTy, ctx);
|
|
cs.cacheExprTypes(base);
|
|
|
|
return buildMemberRef(base, SourceLoc(), overload, loc, locator,
|
|
locator, implicit, semantics);
|
|
}
|
|
|
|
if (auto *typeDecl = dyn_cast<TypeDecl>(decl)) {
|
|
if (!isa<ModuleDecl>(decl)) {
|
|
TypeExpr *typeExpr = nullptr;
|
|
if (implicit) {
|
|
typeExpr = TypeExpr::createImplicitHack(
|
|
loc.getBaseNameLoc(), adjustedFullType->getMetatypeInstanceType(), ctx);
|
|
} else {
|
|
typeExpr = TypeExpr::createForDecl(loc, typeDecl, dc);
|
|
typeExpr->setType(adjustedFullType);
|
|
}
|
|
cs.cacheType(typeExpr);
|
|
return typeExpr;
|
|
}
|
|
}
|
|
|
|
auto ref = resolveConcreteDeclRef(decl, locator);
|
|
|
|
// If we have a variable that's treated as an rvalue but allows
|
|
// assignment (for initialization) in the current context,
|
|
// treat it as an rvalue that we immediately load. This is
|
|
// the AST that's expected by SILGen.
|
|
bool loadImmediately = false;
|
|
if (auto var = dyn_cast_or_null<VarDecl>(ref.getDecl())) {
|
|
if (!fullType->hasLValueType()) {
|
|
if (var->mutability(dc) == StorageMutability::Initializable) {
|
|
fullType = LValueType::get(fullType);
|
|
adjustedFullType = LValueType::get(adjustedFullType);
|
|
loadImmediately = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
auto declRefExpr =
|
|
new (ctx) DeclRefExpr(ref, loc, implicit, semantics, fullType);
|
|
cs.cacheType(declRefExpr);
|
|
declRefExpr->setFunctionRefInfo(choice.getFunctionRefInfo());
|
|
|
|
Expr *result = forceUnwrapIfExpected(declRefExpr, locator);
|
|
|
|
if (auto *fnDecl = dyn_cast<AbstractFunctionDecl>(decl)) {
|
|
if (AnyFunctionRef(fnDecl).hasExternalPropertyWrapperParameters() &&
|
|
declRefExpr->getFunctionRefInfo().isUnapplied()) {
|
|
// We don't need to do any further adjustment once we've built the
|
|
// curry thunk.
|
|
return buildSingleCurryThunk(result, fnDecl,
|
|
adjustedFullType->castTo<FunctionType>(),
|
|
adjustedFullType->castTo<FunctionType>(),
|
|
locator);
|
|
}
|
|
}
|
|
|
|
result = adjustTypeForDeclReference(result, fullType, adjustedFullType,
|
|
locator);
|
|
// If we have to load, do so now.
|
|
if (loadImmediately)
|
|
result = cs.addImplicitLoadExpr(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// Describes an opened existential that has not yet been closed.
|
|
struct OpenedExistential {
|
|
/// The archetype describing this opened existential.
|
|
ExistentialArchetypeType *Archetype;
|
|
|
|
/// The existential value being opened.
|
|
Expr *ExistentialValue;
|
|
|
|
/// The opaque value (of archetype type) stored within the
|
|
/// existential.
|
|
OpaqueValueExpr *OpaqueValue;
|
|
|
|
/// The depth of this currently-opened existential. Once the
|
|
/// depth of the expression stack is equal to this value, the
|
|
/// existential can be closed.
|
|
unsigned Depth;
|
|
};
|
|
|
|
/// A stack of opened existentials that have not yet been closed.
|
|
/// Ordered by decreasing depth.
|
|
llvm::SmallVector<OpenedExistential, 2> OpenedExistentials;
|
|
|
|
/// A stack of expressions being walked, used to compute existential depth.
|
|
llvm::SmallVector<Expr *, 8> ExprStack;
|
|
|
|
/// A map of apply exprs to their callee locators. This is necessary
|
|
/// because after rewriting an apply's function expr, its callee locator
|
|
/// will no longer be equivalent to the one stored in the solution.
|
|
llvm::DenseMap<ApplyExpr *, ConstraintLocator *> CalleeLocators;
|
|
|
|
/// A cache of decl references with their contextual substitutions for a
|
|
/// given callee locator.
|
|
llvm::DenseMap<ConstraintLocator *, ConcreteDeclRef> CachedConcreteRefs;
|
|
|
|
/// Resolves the contextual substitutions for a reference to a declaration
|
|
/// at a given locator. This should be preferred to
|
|
/// Solution::resolveConcreteDeclRef as it caches the result.
|
|
ConcreteDeclRef
|
|
resolveConcreteDeclRef(ValueDecl *decl, ConstraintLocatorBuilder locator) {
|
|
if (!decl)
|
|
return ConcreteDeclRef();
|
|
|
|
// Cache the resulting concrete reference. Ideally this would be done on
|
|
// Solution, however unfortunately that would require a const_cast which
|
|
// would be undefined behaviour if we ever had a `const Solution`.
|
|
auto *loc = getConstraintSystem().getConstraintLocator(locator);
|
|
auto &ref = CachedConcreteRefs[loc];
|
|
if (!ref)
|
|
ref = solution.resolveConcreteDeclRef(decl, loc);
|
|
|
|
return ref;
|
|
}
|
|
|
|
/// Members which are AbstractFunctionDecls but not FuncDecls cannot
|
|
/// mutate self.
|
|
bool isNonMutatingMember(ValueDecl *member) {
|
|
if (!isa<AbstractFunctionDecl>(member))
|
|
return false;
|
|
return !isa<FuncDecl>(member) || !cast<FuncDecl>(member)->isMutating();
|
|
}
|
|
|
|
/// If the expression might be a dynamic method call, return the base
|
|
/// value for the call.
|
|
Expr *getBaseExpr(Expr *expr) {
|
|
// Keep going up as long as this expression is the parent's base.
|
|
if (auto unresolvedDot = dyn_cast<UnresolvedDotExpr>(expr)) {
|
|
return unresolvedDot->getBase();
|
|
// Remaining cases should only come up when we're re-typechecking.
|
|
// FIXME: really it would be much better if Sema had stricter phase
|
|
// separation.
|
|
} else if (auto selfApply = dyn_cast<SelfApplyExpr>(expr)) {
|
|
return selfApply->getBase();
|
|
} else if (auto apply = dyn_cast<ApplyExpr>(expr)) {
|
|
return apply->getFn();
|
|
} else if (auto lookupRef = dyn_cast<LookupExpr>(expr)) {
|
|
return lookupRef->getBase();
|
|
} else if (auto load = dyn_cast<LoadExpr>(expr)) {
|
|
return load->getSubExpr();
|
|
} else if (auto inout = dyn_cast<InOutExpr>(expr)) {
|
|
return inout->getSubExpr();
|
|
} else if (auto force = dyn_cast<ForceValueExpr>(expr)) {
|
|
return force->getSubExpr();
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
/// Calculates the nesting depth of the current application.
|
|
unsigned getArgCount(unsigned maxArgCount) {
|
|
// FIXME: Walking over the ExprStack to figure out the number of argument
|
|
// lists being applied is brittle. We should instead be checking
|
|
// hasAppliedSelf to figure out if the self param is applied, and looking
|
|
// at the FunctionRefInfo to see if the parameter list is applied.
|
|
unsigned e = ExprStack.size();
|
|
unsigned argCount;
|
|
|
|
// Starting from the current expression, count up if the expression is
|
|
// equal to its parent expression's base.
|
|
Expr *prev = ExprStack.back();
|
|
|
|
for (argCount = 1; argCount < maxArgCount && argCount < e; ++argCount) {
|
|
Expr *result = ExprStack[e - argCount - 1];
|
|
Expr *base = getBaseExpr(result);
|
|
if (base != prev)
|
|
break;
|
|
prev = result;
|
|
}
|
|
|
|
return argCount;
|
|
}
|
|
|
|
/// Open an existential value into a new, opaque value of
|
|
/// archetype type.
|
|
///
|
|
/// \param base An expression of existential type whose value will
|
|
/// be opened.
|
|
///
|
|
/// \param archetype The archetype that describes the opened existential
|
|
/// type.
|
|
///
|
|
/// \param member The member that is being referenced on the existential
|
|
/// type.
|
|
///
|
|
/// \returns An OpaqueValueExpr that provides a reference to the value
|
|
/// stored within the expression or its metatype (if the base was a
|
|
/// metatype).
|
|
Expr *openExistentialReference(Expr *base,
|
|
ExistentialArchetypeType *archetype,
|
|
ValueDecl *member, SourceLoc memberLoc) {
|
|
assert(archetype && "archetype not already opened?");
|
|
|
|
// Dig out the base type.
|
|
Type baseTy = cs.getType(base);
|
|
|
|
// Look through inout.
|
|
bool isLValue = false;
|
|
InOutExpr *origInOutBase = dyn_cast<InOutExpr>(base);
|
|
if (origInOutBase) {
|
|
base = origInOutBase->getSubExpr();
|
|
baseTy = baseTy->getInOutObjectType();
|
|
isLValue = true;
|
|
}
|
|
|
|
// Look through lvalues.
|
|
if (auto lvalueTy = baseTy->getAs<LValueType>()) {
|
|
isLValue = true;
|
|
baseTy = lvalueTy->getObjectType();
|
|
}
|
|
|
|
// Look through metatypes.
|
|
bool isMetatype = false;
|
|
if (auto metaTy = baseTy->getAs<AnyMetatypeType>()) {
|
|
isMetatype = true;
|
|
baseTy = metaTy->getInstanceType();
|
|
}
|
|
|
|
assert(baseTy->isAnyExistentialType() && "Type must be existential");
|
|
|
|
// Embedded Swift has limitations on the use of generic members of
|
|
// existentials. Diagnose them here.
|
|
diagnoseGenericMemberOfExistentialInEmbedded(
|
|
dc, memberLoc, baseTy, member);
|
|
|
|
// If the base was an lvalue but it will only be treated as an
|
|
// rvalue, turn the base into an rvalue now. This results in
|
|
// better SILGen.
|
|
if (isLValue && !origInOutBase &&
|
|
(isNonMutatingMember(member) ||
|
|
member->getDeclContext()->getDeclaredInterfaceType()
|
|
->hasReferenceSemantics())) {
|
|
base = cs.coerceToRValue(base);
|
|
isLValue = false;
|
|
}
|
|
|
|
// Determine the number of applications that need to occur before
|
|
// we can close this existential, and record it.
|
|
unsigned maxArgCount = member->getNumCurryLevels();
|
|
unsigned depth = ExprStack.size() - getArgCount(maxArgCount);
|
|
|
|
// Invalid case -- direct call of a metatype. Has one less argument
|
|
// application because there's no ".init".
|
|
if (isa<ApplyExpr>(ExprStack.back()))
|
|
depth++;
|
|
|
|
// Create the opaque opened value. If we started with a
|
|
// metatype, it's a metatype.
|
|
Type opaqueType = archetype;
|
|
if (isMetatype)
|
|
opaqueType = MetatypeType::get(opaqueType);
|
|
if (isLValue)
|
|
opaqueType = LValueType::get(opaqueType);
|
|
|
|
auto archetypeVal =
|
|
new (ctx) OpaqueValueExpr(base->getSourceRange(), opaqueType);
|
|
cs.cacheType(archetypeVal);
|
|
|
|
// Record the opened existential.
|
|
OpenedExistentials.push_back({archetype, base, archetypeVal, depth});
|
|
|
|
// Re-apply inout if needed.
|
|
Expr *resultExpr = archetypeVal;
|
|
if (origInOutBase) {
|
|
resultExpr = new (ctx) InOutExpr(
|
|
origInOutBase->getLoc(), resultExpr, opaqueType->getRValueType());
|
|
cs.cacheType(resultExpr);
|
|
}
|
|
|
|
return resultExpr;
|
|
}
|
|
|
|
/// Try to close the innermost active existential, if there is one.
|
|
bool closeExistential(Expr *&result, ConstraintLocatorBuilder locator,
|
|
bool force) {
|
|
if (OpenedExistentials.empty())
|
|
return false;
|
|
|
|
auto &record = OpenedExistentials.back();
|
|
assert(record.Depth <= ExprStack.size());
|
|
|
|
if (!force && record.Depth < ExprStack.size() - 1)
|
|
return false;
|
|
|
|
// If we had a return type of 'Self', erase it.
|
|
Type resultTy;
|
|
resultTy = cs.getType(result);
|
|
|
|
auto *env = record.Archetype->getGenericEnvironment();
|
|
|
|
if (resultTy->hasLocalArchetypeFromEnvironment(env)) {
|
|
Type erasedTy = typeEraseOpenedArchetypesFromEnvironment(resultTy, env);
|
|
ASSERT(erasedTy.getPointer() != resultTy.getPointer());
|
|
|
|
// We currently cannot keep lvalueness if the object type changed.
|
|
if (auto *lvalueTy = dyn_cast<LValueType>(erasedTy.getPointer())) {
|
|
erasedTy = lvalueTy->getObjectType();
|
|
}
|
|
|
|
auto range = result->getSourceRange();
|
|
result = coerceToType(result, erasedTy, locator);
|
|
// FIXME: Implement missing tuple-to-tuple conversion
|
|
if (result == nullptr) {
|
|
result = new (ctx) ErrorExpr(range);
|
|
cs.setType(result, erasedTy);
|
|
// The opaque value is no longer reachable in an AST walk as
|
|
// a result of the result above being replaced with an
|
|
// ErrorExpr, but there is code expecting to have a type set
|
|
// on it. Since we no longer have a reachable reference,
|
|
// we'll null this out.
|
|
record.OpaqueValue = nullptr;
|
|
}
|
|
}
|
|
|
|
// Form the open-existential expression.
|
|
result = new (ctx) OpenExistentialExpr(
|
|
record.ExistentialValue,
|
|
record.OpaqueValue,
|
|
result, cs.getType(result));
|
|
cs.cacheType(result);
|
|
|
|
OpenedExistentials.pop_back();
|
|
return true;
|
|
}
|
|
|
|
/// Close any active existentials.
|
|
bool closeExistentials(Expr *&result, ConstraintLocatorBuilder locator,
|
|
bool force=false) {
|
|
bool closedAny = false;
|
|
while (closeExistential(result, locator, force)) {
|
|
force = false;
|
|
closedAny = true;
|
|
}
|
|
|
|
return closedAny;
|
|
}
|
|
|
|
/// When we have a reference to a declaration whose type in context is
|
|
/// different from its normal interface type, introduce the appropriate
|
|
/// conversions. This can happen due to `@preconcurrency`.
|
|
Expr *adjustTypeForDeclReference(
|
|
Expr *expr, Type openedType, Type adjustedOpenedType,
|
|
ConstraintLocatorBuilder locator,
|
|
llvm::function_ref<Type(Type)> getNewType = [](Type type) {
|
|
return type;
|
|
}) {
|
|
// If the types are the same, do nothing.
|
|
if (openedType->isEqual(adjustedOpenedType))
|
|
return expr;
|
|
|
|
// For an RValue function type, use a standard function conversion.
|
|
if (openedType->is<AnyFunctionType>()) {
|
|
expr = new (ctx) FunctionConversionExpr(
|
|
expr, getNewType(adjustedOpenedType));
|
|
cs.cacheType(expr);
|
|
return expr;
|
|
}
|
|
|
|
// For any kind of LValue, use an ABISafeConversion.
|
|
if (openedType->hasLValueType()) {
|
|
assert(adjustedOpenedType->hasLValueType() && "lvalueness mismatch?");
|
|
|
|
expr = new (ctx) ABISafeConversionExpr(
|
|
expr, getNewType(adjustedOpenedType));
|
|
cs.cacheType(expr);
|
|
return expr;
|
|
}
|
|
|
|
// If we have an optional type, wrap it up in a monadic '?' and recurse.
|
|
if (Type objectType = openedType->getOptionalObjectType()) {
|
|
Type adjustedRefType = getNewType(adjustedOpenedType);
|
|
Type adjustedObjectType = adjustedRefType->getOptionalObjectType();
|
|
assert(adjustedObjectType && "Not an optional?");
|
|
|
|
expr = new (ctx) BindOptionalExpr(expr, SourceLoc(), 0, objectType);
|
|
cs.cacheType(expr);
|
|
expr = adjustTypeForDeclReference(
|
|
expr, objectType, adjustedObjectType, locator);
|
|
expr = new (ctx) InjectIntoOptionalExpr(expr, adjustedRefType);
|
|
cs.cacheType(expr);
|
|
expr = new (ctx) OptionalEvaluationExpr(expr, adjustedRefType);
|
|
cs.cacheType(expr);
|
|
return expr;
|
|
}
|
|
|
|
return coerceToType(expr, adjustedOpenedType, locator);
|
|
}
|
|
|
|
/// Determines if a partially-applied member reference should be
|
|
/// converted into a fully-applied member reference with a pair of
|
|
/// closures.
|
|
bool shouldBuildCurryThunk(OverloadChoice choice,
|
|
bool baseIsInstance) {
|
|
ValueDecl *member = choice.getDecl();
|
|
|
|
// If we're inside a selector expression, don't build the thunk.
|
|
// Were not actually going to emit the member reference, just
|
|
// look at the AST.
|
|
for (auto expr : ExprStack)
|
|
if (isa<ObjCSelectorExpr>(expr))
|
|
return false;
|
|
|
|
// Unbound instance method references always build a thunk, even if
|
|
// we apply the arguments (eg, SomeClass.method(self)(a)), to avoid
|
|
// representational issues.
|
|
if (!baseIsInstance && member->isInstanceMember())
|
|
return true;
|
|
|
|
// Bound member references that are '@objc optional' or found via dynamic
|
|
// lookup are always represented via DynamicMemberRefExpr instead of a
|
|
// curry thunk.
|
|
if (member->getAttrs().hasAttribute<OptionalAttr>() ||
|
|
choice.getKind() == OverloadChoiceKind::DeclViaDynamic)
|
|
return false;
|
|
|
|
// Figure out how many argument lists we need.
|
|
unsigned maxArgCount = member->getNumCurryLevels();
|
|
|
|
unsigned argCount = [&]() -> unsigned {
|
|
Expr *prev = ExprStack.back();
|
|
|
|
// FIXME: Representational gunk because "T(...)" is really
|
|
// "T.init(...)" -- pretend it has two argument lists like
|
|
// a real '.' call.
|
|
if (isa<ConstructorDecl>(member) &&
|
|
isa<CallExpr>(prev) &&
|
|
isa<TypeExpr>(cast<CallExpr>(prev)->getFn())) {
|
|
assert(maxArgCount == 2);
|
|
return 2;
|
|
}
|
|
|
|
// Similarly, ".foo(...)" really applies two argument lists.
|
|
if (isa<CallExpr>(prev) &&
|
|
isa<UnresolvedMemberExpr>(cast<CallExpr>(prev)->getFn()))
|
|
return 2;
|
|
|
|
return getArgCount(maxArgCount);
|
|
}();
|
|
|
|
// If we have fewer argument lists than expected, build a thunk.
|
|
if (argCount < maxArgCount)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Build the call inside the body of a single curry thunk
|
|
/// "{ args in base.fn(args) }".
|
|
///
|
|
/// \param baseExpr The captured base expression, if warranted.
|
|
/// \param fnExpr The expression to be called by consecutively applying
|
|
/// the optional \p baseExpr and thunk parameters.
|
|
/// \param declOrClosure The underlying function-like declaration or
|
|
/// closure we're going to call.
|
|
/// \param thunkParamList The enclosing thunk's parameter list.
|
|
/// \param locator The locator pinned on the function reference carried
|
|
/// by \p fnExpr. If the function has associated applied property wrappers,
|
|
/// the locator is used to pull them in.
|
|
ApplyExpr *buildSingleCurryThunkBodyCall(Expr *baseExpr, Expr *fnExpr,
|
|
DeclContext *declOrClosure,
|
|
ParameterList *thunkParamList,
|
|
ConstraintLocatorBuilder locator) {
|
|
auto *const fnTy = cs.getType(fnExpr)->castTo<FunctionType>();
|
|
auto *calleeFnTy = fnTy;
|
|
|
|
if (baseExpr) {
|
|
// Coerce the base expression to the container type.
|
|
const auto calleeSelfParam = calleeFnTy->getParams().front();
|
|
baseExpr =
|
|
coerceToType(baseExpr, calleeSelfParam.getOldType(), locator);
|
|
|
|
// Uncurry the callee type in the presence of a base expression; we
|
|
// want '(args) -> result' vs. '(self) -> (args) -> result'.
|
|
calleeFnTy = calleeFnTy->getResult()->castTo<FunctionType>();
|
|
}
|
|
|
|
auto appliedPropertyWrappers =
|
|
solution.getAppliedPropertyWrappers(locator.getAnchor());
|
|
const auto calleeDeclRef = resolveConcreteDeclRef(
|
|
dyn_cast<AbstractFunctionDecl>(declOrClosure), locator);
|
|
|
|
auto *const calleeParamList = getParameterList(declOrClosure);
|
|
const auto calleeParams = calleeFnTy->getParams();
|
|
|
|
// Rebuild the callee params: SILGen knows how to emit property-wrapped
|
|
// parameters, but the corresponding parameter types need to match the
|
|
// backing wrapper types.
|
|
SmallVector<AnyFunctionType::Param, 4> newCalleeParams;
|
|
newCalleeParams.reserve(calleeParams.size());
|
|
|
|
// Build the argument list for the call.
|
|
SmallVector<Argument, 4> args;
|
|
unsigned appliedWrapperIndex = 0;
|
|
for (const auto idx : indices(*thunkParamList)) {
|
|
auto *const thunkParamDecl = thunkParamList->get(idx);
|
|
|
|
const auto calleeParam = calleeParams[idx];
|
|
const auto calleeParamType = calleeParam.getParameterType();
|
|
const auto thunkParamType = thunkParamDecl->getTypeInContext();
|
|
|
|
Expr *paramRef = new (ctx)
|
|
DeclRefExpr(thunkParamDecl, DeclNameLoc(), /*implicit*/ true);
|
|
paramRef->setType(thunkParamDecl->isInOut()
|
|
? LValueType::get(thunkParamType)
|
|
: thunkParamType);
|
|
cs.cacheType(paramRef);
|
|
|
|
paramRef = coerceToType(paramRef,
|
|
thunkParamDecl->isInOut()
|
|
? LValueType::get(calleeParamType)
|
|
: calleeParamType,
|
|
locator);
|
|
|
|
auto *const calleeParamDecl = calleeParamList->get(idx);
|
|
if (calleeParamDecl->hasExternalPropertyWrapper()) {
|
|
// Rewrite the parameter ref to the backing wrapper initialization
|
|
// expression.
|
|
auto &appliedWrapper = appliedPropertyWrappers[appliedWrapperIndex++];
|
|
|
|
using ValueKind = AppliedPropertyWrapperExpr::ValueKind;
|
|
ValueKind valueKind = (appliedWrapper.initKind ==
|
|
PropertyWrapperInitKind::ProjectedValue
|
|
? ValueKind::ProjectedValue
|
|
: ValueKind::WrappedValue);
|
|
|
|
paramRef = AppliedPropertyWrapperExpr::create(
|
|
ctx, calleeDeclRef, calleeParamDecl, SourceLoc(),
|
|
appliedWrapper.wrapperType, paramRef, valueKind);
|
|
cs.cacheExprTypes(paramRef);
|
|
|
|
newCalleeParams.push_back(calleeParam.withType(paramRef->getType()));
|
|
|
|
// TODO: inout
|
|
// FIXME: vararg
|
|
} else {
|
|
if (thunkParamDecl->isInOut()) {
|
|
paramRef =
|
|
new (ctx) InOutExpr(SourceLoc(), paramRef, calleeParamType,
|
|
/*implicit=*/true);
|
|
} else if (thunkParamDecl->isVariadic()) {
|
|
assert(calleeParamType->isEqual(paramRef->getType()));
|
|
paramRef = VarargExpansionExpr::createParamExpansion(ctx, paramRef);
|
|
}
|
|
cs.cacheType(paramRef);
|
|
|
|
newCalleeParams.push_back(calleeParam);
|
|
}
|
|
|
|
args.emplace_back(SourceLoc(), calleeParam.getLabel(), paramRef);
|
|
}
|
|
|
|
ASSERT(appliedWrapperIndex == appliedPropertyWrappers.size());
|
|
|
|
// SILGen knows how to emit property-wrapped parameters, but the
|
|
// corresponding parameter types need to match the backing wrapper types.
|
|
// To handle this, build a new callee function type out of the adjusted
|
|
// callee params, hand it over to the conditional 'self' call, and use it
|
|
// to update the type of the called expression with respect to whether
|
|
// it's 'self'-curried.
|
|
auto *const newCalleeFnTy = FunctionType::get(
|
|
newCalleeParams, calleeFnTy->getResult(), calleeFnTy->getExtInfo());
|
|
|
|
// If given, apply the base expression to the curried 'self'
|
|
// parameter first.
|
|
if (baseExpr) {
|
|
fnExpr->setType(FunctionType::get(fnTy->getParams(), newCalleeFnTy,
|
|
fnTy->getExtInfo()));
|
|
cs.cacheType(fnExpr);
|
|
|
|
fnExpr = DotSyntaxCallExpr::create(ctx, fnExpr, SourceLoc(),
|
|
Argument::unlabeled(baseExpr));
|
|
}
|
|
fnExpr->setType(newCalleeFnTy);
|
|
cs.cacheType(fnExpr);
|
|
|
|
// Finally, apply the argument list to the callee.
|
|
ApplyExpr *callExpr = CallExpr::createImplicit(
|
|
ctx, fnExpr, ArgumentList::createImplicit(ctx, args));
|
|
callExpr->setType(calleeFnTy->getResult());
|
|
cs.cacheType(callExpr);
|
|
|
|
return callExpr;
|
|
}
|
|
|
|
std::optional<ActorIsolation>
|
|
getIsolationFromFunctionType(FunctionType *thunkTy) {
|
|
switch (thunkTy->getIsolation().getKind()) {
|
|
case FunctionTypeIsolation::Kind::NonIsolated:
|
|
case FunctionTypeIsolation::Kind::Parameter:
|
|
case FunctionTypeIsolation::Kind::Erased:
|
|
return {};
|
|
case FunctionTypeIsolation::Kind::GlobalActor:
|
|
return ActorIsolation::forGlobalActor(
|
|
thunkTy->getIsolation().getGlobalActorType());
|
|
case FunctionTypeIsolation::Kind::NonIsolatedNonsending:
|
|
return ActorIsolation::forCallerIsolationInheriting();
|
|
}
|
|
}
|
|
|
|
/// Build a "{ args in base.fn(args) }" single-expression curry thunk.
|
|
///
|
|
/// \param baseExpr The base expression to be captured, if warranted.
|
|
/// \param fnExpr The expression to be called by consecutively applying
|
|
/// the optional \p baseExpr and thunk parameters.
|
|
/// \param declOrClosure The underlying function-like declaration or
|
|
/// closure we're going to call.
|
|
/// \param thunkTy The type of the thunk.
|
|
/// \param locator The locator pinned on the function reference carried
|
|
/// by \p fnExpr. If the function has associated applied property wrappers,
|
|
/// the locator is used to pull them in.
|
|
AutoClosureExpr *buildSingleCurryThunk(Expr *baseExpr, Expr *fnExpr,
|
|
DeclContext *declOrClosure,
|
|
FunctionType *thunkTy,
|
|
FunctionType *refTy,
|
|
ConstraintLocatorBuilder locator) {
|
|
const OptionSet<ParameterList::CloneFlags> options =
|
|
(ParameterList::Implicit | ParameterList::NamedArguments);
|
|
auto *const thunkParamList =
|
|
getParameterList(declOrClosure)->clone(ctx, options);
|
|
|
|
for (const auto idx : indices(*thunkParamList)) {
|
|
auto *param = thunkParamList->get(idx);
|
|
auto arg = thunkTy->getParams()[idx];
|
|
|
|
param->setInterfaceType(arg.getParameterType()->mapTypeOutOfContext());
|
|
param->setSpecifier(ParamDecl::getParameterSpecifierForValueOwnership(
|
|
arg.getValueOwnership()));
|
|
}
|
|
|
|
auto *const thunk =
|
|
new (ctx) AutoClosureExpr(/*set body later*/ nullptr, thunkTy, dc);
|
|
thunk->setParameterList(thunkParamList);
|
|
thunk->setThunkKind(AutoClosureExpr::Kind::SingleCurryThunk);
|
|
|
|
// TODO: We should do this in the ActorIsolation checker but for now it is
|
|
// too risky. This is a narrow fix for 6.2.
|
|
//
|
|
// The problem that this is working around is given an autoclosure that
|
|
// returns an autoclosure that partially applies over an instance method,
|
|
// we do not visit the inner autoclosure in the ActorIsolation checker
|
|
// meaning that we do not properly call setActorIsolation on the
|
|
// AbstractClosureExpr that we produce here.
|
|
if (auto isolation = getIsolationFromFunctionType(thunkTy)) {
|
|
thunk->setActorIsolation(*isolation);
|
|
}
|
|
|
|
cs.cacheType(thunk);
|
|
|
|
// If the `self` type is existential, it must be opened.
|
|
OpaqueValueExpr *baseOpened = nullptr;
|
|
Expr *origBaseExpr = baseExpr;
|
|
if (baseExpr) {
|
|
auto baseTy = cs.getType(baseExpr);
|
|
if (baseTy->isAnyExistentialType()) {
|
|
Type openedTy = solution.OpenedExistentialTypes.lookup(
|
|
cs.getConstraintLocator(locator));
|
|
assert(openedTy);
|
|
|
|
Type opaqueValueTy = openedTy;
|
|
if (baseTy->is<ExistentialMetatypeType>())
|
|
opaqueValueTy = MetatypeType::get(opaqueValueTy);
|
|
|
|
baseOpened = new (ctx) OpaqueValueExpr(SourceLoc(), opaqueValueTy);
|
|
cs.cacheType(baseOpened);
|
|
baseExpr = baseOpened;
|
|
}
|
|
}
|
|
|
|
Expr *thunkBody = buildSingleCurryThunkBodyCall(
|
|
baseExpr, fnExpr, declOrClosure, thunkParamList, locator);
|
|
|
|
// If we called a function with a dynamic 'Self' result, we may need some
|
|
// special handling.
|
|
if (baseExpr) {
|
|
if (auto *fnDecl = dyn_cast<AbstractFunctionDecl>(declOrClosure)) {
|
|
if (fnDecl->getDeclContext()->getSelfClassDecl()) {
|
|
auto convTy = refTy->getResult();
|
|
if (!thunkBody->getType()->isEqual(convTy)) {
|
|
thunkBody = cs.cacheType(
|
|
new (ctx) CovariantReturnConversionExpr(thunkBody, convTy));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now, coerce to the result type of the thunk.
|
|
thunkBody = coerceToType(thunkBody, thunkTy->getResult(), locator);
|
|
|
|
// Close up the existential if necessary.
|
|
if (baseOpened) {
|
|
thunkBody = new (ctx) OpenExistentialExpr(origBaseExpr,
|
|
baseOpened,
|
|
thunkBody,
|
|
thunkBody->getType());
|
|
cs.cacheType(thunkBody);
|
|
}
|
|
|
|
if (thunkTy->getExtInfo().isThrowing()) {
|
|
thunkBody = new (ctx)
|
|
TryExpr(thunkBody->getStartLoc(), thunkBody, cs.getType(thunkBody),
|
|
/*implicit=*/true);
|
|
cs.cacheType(thunkBody);
|
|
}
|
|
|
|
thunk->setBody(thunkBody);
|
|
|
|
return thunk;
|
|
}
|
|
|
|
/// Build a "{ args in fn(args) }" single-expression curry thunk.
|
|
///
|
|
/// \param fnExpr The expression to be called by applying the thunk
|
|
/// parameters.
|
|
/// \param declOrClosure The underlying function-like declaration or
|
|
/// closure we're going to call.
|
|
/// \param thunkTy The type of the resulting thunk. This should be the
|
|
/// type of the \c fnExpr, with any potential adjustments for things like
|
|
/// concurrency.
|
|
/// \param refTy The type of the declaration reference inside the thunk.
|
|
/// This might involve opened existentials or a covariant
|
|
/// Self result.
|
|
/// \param locator The locator pinned on the function reference carried
|
|
/// by \p fnExpr. If the function has associated applied property wrappers,
|
|
/// the locator is used to pull them in.
|
|
AutoClosureExpr *buildSingleCurryThunk(Expr *fnExpr,
|
|
DeclContext *declOrClosure,
|
|
FunctionType *thunkTy,
|
|
FunctionType *refTy,
|
|
ConstraintLocatorBuilder locator) {
|
|
return buildSingleCurryThunk(/*baseExpr=*/nullptr, fnExpr, declOrClosure,
|
|
thunkTy, refTy, locator);
|
|
}
|
|
|
|
/// Build a "{ self in { args in self.fn(args) } }" nested curry thunk.
|
|
///
|
|
/// \param memberRef The expression to be called in the inner thunk by
|
|
/// consecutively applying the captured outer thunk's 'self' parameter and
|
|
/// the parameters of the inner thunk.
|
|
/// \param member The underlying function declaration to be called.
|
|
/// \param outerThunkTy The type of the outer thunk.
|
|
/// \param outerRefTy The type of the declaration reference inside the thunk.
|
|
/// This might involve opened existentials or a covariant
|
|
/// Self result.
|
|
/// \param memberLocator The locator pinned on the member reference. If the
|
|
/// function has associated applied property wrappers, the locator is used
|
|
/// to pull them in.
|
|
AutoClosureExpr *
|
|
buildDoubleCurryThunk(DeclRefExpr *memberRef, ValueDecl *member,
|
|
FunctionType *outerThunkTy,
|
|
FunctionType *outerRefTy,
|
|
ConstraintLocatorBuilder memberLocator,
|
|
DeclNameLoc memberLoc, bool isDynamicLookup) {
|
|
const auto selfThunkParam = outerThunkTy->getParams().front();
|
|
const auto selfThunkParamTy = selfThunkParam.getPlainType();
|
|
|
|
// Build the 'self' param for the outer thunk, "{ self in ... }".
|
|
auto *const selfParamDecl =
|
|
new (ctx) ParamDecl(SourceLoc(),
|
|
/*argument label*/ SourceLoc(), Identifier(),
|
|
/*parameter name*/ SourceLoc(), ctx.Id_self, dc);
|
|
selfParamDecl->setInterfaceType(selfThunkParamTy->mapTypeOutOfContext());
|
|
selfParamDecl->setSpecifier(
|
|
ParamDecl::getParameterSpecifierForValueOwnership(
|
|
selfThunkParam.getValueOwnership()));
|
|
selfParamDecl->setImplicit();
|
|
|
|
// Build a reference to the 'self' parameter.
|
|
Expr *selfParamRef = new (ctx) DeclRefExpr(selfParamDecl, DeclNameLoc(),
|
|
/*implicit=*/true);
|
|
selfParamRef->setType(selfThunkParam.isInOut()
|
|
? LValueType::get(selfThunkParamTy)
|
|
: selfThunkParamTy);
|
|
cs.cacheType(selfParamRef);
|
|
|
|
if (selfThunkParam.isInOut()) {
|
|
selfParamRef =
|
|
new (ctx) InOutExpr(SourceLoc(), selfParamRef, selfThunkParamTy,
|
|
/*implicit=*/true);
|
|
cs.cacheType(selfParamRef);
|
|
}
|
|
|
|
bool hasOpenedExistential = false;
|
|
Expr *selfOpenedRef = selfParamRef;
|
|
|
|
// If the 'self' parameter type is existential, it must be opened.
|
|
if (selfThunkParamTy->isAnyExistentialType()) {
|
|
Type openedTy = solution.OpenedExistentialTypes.lookup(
|
|
cs.getConstraintLocator(memberLocator));
|
|
assert(openedTy);
|
|
|
|
hasOpenedExistential = true;
|
|
|
|
// If we're opening an existential:
|
|
// - The type of 'memberRef' inside the thunk is written in terms of the
|
|
// opened existential archetype.
|
|
// - The type of the thunk is written in terms of the
|
|
// erased existential bounds.
|
|
Type opaqueValueTy = openedTy;
|
|
if (selfThunkParamTy->is<ExistentialMetatypeType>())
|
|
opaqueValueTy = MetatypeType::get(opaqueValueTy);
|
|
|
|
if (selfThunkParam.isInOut())
|
|
opaqueValueTy = LValueType::get(opaqueValueTy);
|
|
|
|
selfOpenedRef = new (ctx) OpaqueValueExpr(SourceLoc(), opaqueValueTy);
|
|
cs.cacheType(selfOpenedRef);
|
|
}
|
|
|
|
auto outerActorIsolation = [&]() -> std::optional<ActorIsolation> {
|
|
auto resultType = outerThunkTy->getResult();
|
|
// Look through all optionals.
|
|
while (auto opt = resultType->getOptionalObjectType())
|
|
resultType = opt;
|
|
auto f =
|
|
getIsolationFromFunctionType(resultType->castTo<FunctionType>());
|
|
if (!f)
|
|
return {};
|
|
|
|
// If we have a non-async function and our "inferred" isolation is
|
|
// caller isolation inheriting, then we do not infer isolation and just
|
|
// use the default. The reason why we are doing this is:
|
|
//
|
|
// 1. nonisolated for synchronous functions is equivalent to
|
|
// nonisolated(nonsending).
|
|
//
|
|
// 2. There is a strong invariant in the compiler today that caller
|
|
// isolation inheriting is always async. By using nonisolated here, we
|
|
// avoid breaking that invariant.
|
|
if (!outerThunkTy->isAsync() && f->isCallerIsolationInheriting())
|
|
return {};
|
|
return f;
|
|
}();
|
|
|
|
Expr *outerThunkBody = nullptr;
|
|
|
|
// For an @objc optional member or a member found via dynamic lookup,
|
|
// build a dynamic member reference. Otherwise, build a nested
|
|
// "{ args... in self.member(args...) }" thunk that calls the member.
|
|
if (isDynamicLookup || member->getAttrs().hasAttribute<OptionalAttr>()) {
|
|
auto *const selfCalleeTy =
|
|
cs.getType(memberRef)->castTo<FunctionType>();
|
|
|
|
outerThunkBody = new (ctx) DynamicMemberRefExpr(
|
|
selfOpenedRef, SourceLoc(),
|
|
resolveConcreteDeclRef(member, memberLocator), memberLoc);
|
|
outerThunkBody->setImplicit(true);
|
|
outerThunkBody->setType(selfCalleeTy->getResult());
|
|
cs.cacheType(outerThunkBody);
|
|
|
|
outerThunkBody = coerceToType(outerThunkBody, outerThunkTy->getResult(),
|
|
memberLocator);
|
|
|
|
// Close the existential if warranted.
|
|
if (hasOpenedExistential) {
|
|
outerThunkBody = new (ctx) OpenExistentialExpr(
|
|
selfParamRef, cast<OpaqueValueExpr>(selfOpenedRef),
|
|
outerThunkBody, outerThunkBody->getType());
|
|
cs.cacheType(outerThunkBody);
|
|
}
|
|
} else {
|
|
auto *innerThunk = buildSingleCurryThunk(
|
|
selfOpenedRef, memberRef, cast<DeclContext>(member),
|
|
outerThunkTy->getResult()->castTo<FunctionType>(),
|
|
outerRefTy->getResult()->castTo<FunctionType>(),
|
|
memberLocator);
|
|
assert((!outerActorIsolation ||
|
|
innerThunk->getActorIsolation().getKind() ==
|
|
outerActorIsolation->getKind()) &&
|
|
"If we have an isolation for our double curry thunk it should "
|
|
"match our single curry thunk");
|
|
|
|
// Rewrite the body to close the existential if warranted.
|
|
if (hasOpenedExistential) {
|
|
auto *body = innerThunk->getSingleExpressionBody();
|
|
body = new (ctx) OpenExistentialExpr(
|
|
selfParamRef, cast<OpaqueValueExpr>(selfOpenedRef), body,
|
|
body->getType());
|
|
cs.cacheType(body);
|
|
|
|
innerThunk->setBody(body);
|
|
}
|
|
|
|
outerThunkBody = innerThunk;
|
|
}
|
|
|
|
// Finally, construct the outer thunk.
|
|
auto *outerThunk =
|
|
new (ctx) AutoClosureExpr(outerThunkBody, outerThunkTy, dc);
|
|
outerThunk->setThunkKind(AutoClosureExpr::Kind::DoubleCurryThunk);
|
|
outerThunk->setParameterList(
|
|
ParameterList::create(ctx, SourceLoc(), selfParamDecl, SourceLoc()));
|
|
|
|
if (outerActorIsolation) {
|
|
outerThunk->setActorIsolation(*outerActorIsolation);
|
|
}
|
|
|
|
cs.cacheType(outerThunk);
|
|
|
|
return outerThunk;
|
|
}
|
|
|
|
Expr *buildStaticCurryThunk(Expr *base, Expr *declRefExpr,
|
|
AbstractFunctionDecl *member,
|
|
FunctionType *curryThunkTy,
|
|
FunctionType *curryRefTy,
|
|
ConstraintLocatorBuilder locator,
|
|
ConstraintLocatorBuilder memberLocator,
|
|
bool openedExistential) {
|
|
if (cs.isStaticallyDerivedMetatype(base)) {
|
|
// Add a useless ".self" to avoid downstream diagnostics.
|
|
base = new (ctx) DotSelfExpr(base, SourceLoc(), base->getEndLoc(),
|
|
cs.getType(base));
|
|
cs.setType(base, base->getType());
|
|
|
|
// Skip the code below -- we're not building an extra level of
|
|
// call by applying the metatype; instead, the closure we just
|
|
// built is the curried reference.
|
|
return buildSingleCurryThunk(
|
|
base, declRefExpr, member,
|
|
curryThunkTy, curryRefTy,
|
|
memberLocator);
|
|
} else {
|
|
// Add a useless ".self" to avoid downstream diagnostics, in case
|
|
// the type ref is still a TypeExpr.
|
|
base = new (ctx) DotSelfExpr(base, SourceLoc(), base->getEndLoc(),
|
|
cs.getType(base));
|
|
// Introduce a capture variable.
|
|
cs.cacheType(base);
|
|
solution.setExprTypes(base);
|
|
auto capture = new (ctx) VarDecl(/*static*/ false,
|
|
VarDecl::Introducer::Let,
|
|
base->getEndLoc(),
|
|
ctx.getIdentifier("$base$"),
|
|
dc);
|
|
capture->setImplicit();
|
|
capture->setInterfaceType(base->getType()->mapTypeOutOfContext());
|
|
|
|
auto *capturePat =
|
|
NamedPattern::createImplicit(ctx, capture, base->getType());
|
|
|
|
auto *captureDecl = PatternBindingDecl::createImplicit(
|
|
ctx, StaticSpellingKind::None, capturePat, base, dc);
|
|
|
|
// Write the closure in terms of the capture.
|
|
auto baseRef = new (ctx)
|
|
DeclRefExpr(capture, DeclNameLoc(base->getLoc()), /*implicit*/ true);
|
|
baseRef->setType(base->getType());
|
|
cs.cacheType(baseRef);
|
|
|
|
auto *closure = buildSingleCurryThunk(
|
|
baseRef, declRefExpr, member,
|
|
curryThunkTy, curryRefTy,
|
|
memberLocator);
|
|
|
|
// Wrap the closure in a capture list.
|
|
auto captureEntry = CaptureListEntry(captureDecl);
|
|
auto captureExpr = CaptureListExpr::create(ctx, captureEntry,
|
|
closure);
|
|
captureExpr->setImplicit();
|
|
captureExpr->setType(cs.getType(closure));
|
|
cs.cacheType(captureExpr);
|
|
|
|
Expr *finalExpr = captureExpr;
|
|
closeExistentials(finalExpr, locator,
|
|
/*force*/ openedExistential);
|
|
return finalExpr;
|
|
}
|
|
}
|
|
|
|
Expr *buildDynamicMemberRef(Expr *base, Type baseTy,
|
|
SourceLoc dotLoc,
|
|
ConcreteDeclRef memberRef,
|
|
Type openedType,
|
|
Type adjustedOpenedType,
|
|
DeclNameLoc memberLoc,
|
|
ConstraintLocatorBuilder locator,
|
|
ConstraintLocatorBuilder memberLocator,
|
|
bool openedExistential,
|
|
bool Implicit) {
|
|
base = cs.coerceToRValue(base);
|
|
Expr *ref = new (ctx) DynamicMemberRefExpr(base, dotLoc, memberRef,
|
|
memberLoc);
|
|
ref->setImplicit(Implicit);
|
|
// FIXME: FunctionRefInfo
|
|
|
|
// Compute the type of the reference.
|
|
auto computeRefType = [&](Type refType) {
|
|
// If the base was an opened existential, erase the opened
|
|
// existential.
|
|
if (openedExistential) {
|
|
refType = typeEraseOpenedArchetypesFromEnvironment(
|
|
refType, baseTy->castTo<ExistentialArchetypeType>()->getGenericEnvironment());
|
|
}
|
|
|
|
return refType;
|
|
};
|
|
|
|
Type refType = computeRefType(openedType);
|
|
cs.setType(ref, refType);
|
|
|
|
// Adjust the declaration reference type, if required.
|
|
ref = adjustTypeForDeclReference(
|
|
ref, openedType, adjustedOpenedType, locator, computeRefType);
|
|
|
|
closeExistentials(ref, locator, /*force=*/openedExistential);
|
|
|
|
// We also need to handle the implicitly unwrap of the result
|
|
// of the called function if that's the type checking solution
|
|
// we ended up with.
|
|
return forceUnwrapIfExpected(ref, memberLocator);
|
|
}
|
|
|
|
/// FIXME: Simplify this horrible parameter list.
|
|
Expr *buildVarMemberRef(Expr *base, SourceLoc dotLoc,
|
|
bool baseIsInstance,
|
|
ConcreteDeclRef memberRef,
|
|
DeclNameLoc memberLoc,
|
|
Type containerTy,
|
|
Type refTy,
|
|
Type refTySelf,
|
|
Type adjustedRefTy,
|
|
Type adjustedRefTySelf,
|
|
AccessSemantics semantics,
|
|
ConstraintLocatorBuilder locator,
|
|
ConstraintLocatorBuilder memberLocator,
|
|
bool isSuper,
|
|
bool isUnboundInstanceMember,
|
|
bool Implicit) {
|
|
auto *varDecl = cast<VarDecl>(memberRef.getDecl());
|
|
|
|
bool loadImmediately = false;
|
|
auto resultType = [&loadImmediately](Type fnTy) -> Type {
|
|
Type resultTy = fnTy->castTo<FunctionType>()->getResult();
|
|
if (loadImmediately)
|
|
return LValueType::get(resultTy);
|
|
return resultTy;
|
|
};
|
|
|
|
// If we have an instance property that's treated as an rvalue
|
|
// but allows assignment (for initialization) in the current
|
|
// context, treat it as an rvalue that we immediately load.
|
|
// This is the AST that's expected by SILGen.
|
|
if (baseIsInstance && !resultType(refTy)->hasLValueType() &&
|
|
varDecl->mutability(dc, dyn_cast<DeclRefExpr>(base))
|
|
== StorageMutability::Initializable) {
|
|
loadImmediately = true;
|
|
}
|
|
|
|
if (isUnboundInstanceMember) {
|
|
assert(memberLocator.getBaseLocator() &&
|
|
cs.UnevaluatedRootExprs.count(
|
|
getAsExpr(memberLocator.getBaseLocator()->getAnchor())) &&
|
|
"Attempt to reference an instance member of a metatype");
|
|
auto baseInstanceTy = cs.getType(base)
|
|
->getInOutObjectType()->getMetatypeInstanceType();
|
|
base = new (ctx) UnevaluatedInstanceExpr(base, baseInstanceTy);
|
|
cs.cacheType(base);
|
|
base->setImplicit();
|
|
}
|
|
|
|
auto memberRefExpr
|
|
= new (ctx) MemberRefExpr(base, dotLoc, memberRef,
|
|
memberLoc, Implicit, semantics);
|
|
memberRefExpr->setIsSuper(isSuper);
|
|
|
|
auto resultTySelf = resultType(refTySelf);
|
|
cs.setType(memberRefExpr, resultTySelf);
|
|
|
|
Expr *result = memberRefExpr;
|
|
result = adjustTypeForDeclReference(result, resultTySelf,
|
|
resultType(adjustedRefTySelf),
|
|
locator);
|
|
closeExistentials(result, locator);
|
|
|
|
// If the property is of dynamic 'Self' type, wrap an implicit
|
|
// conversion around the resulting expression, with the destination
|
|
// type having 'Self' swapped for the appropriate replacement
|
|
// type -- usually the base object type.
|
|
auto resultTy = resultType(refTy);
|
|
if (!resultTy->isEqual(resultTySelf)) {
|
|
result = cs.cacheType(new (ctx) CovariantReturnConversionExpr(
|
|
result, resultTy));
|
|
}
|
|
|
|
// If we need to load, do so now.
|
|
if (loadImmediately) {
|
|
result = cs.addImplicitLoadExpr(result);
|
|
}
|
|
|
|
return forceUnwrapIfExpected(result, memberLocator);
|
|
}
|
|
|
|
/// Build a new member reference with the given base and member.
|
|
Expr *buildMemberRef(Expr *base, SourceLoc dotLoc,
|
|
SelectedOverload overload, DeclNameLoc memberLoc,
|
|
ConstraintLocatorBuilder locator,
|
|
ConstraintLocatorBuilder memberLocator, bool Implicit,
|
|
AccessSemantics semantics) {
|
|
const auto &choice = overload.choice;
|
|
const auto openedType = simplifyType(overload.openedType);
|
|
const auto adjustedOpenedType = simplifyType(overload.adjustedOpenedType);
|
|
|
|
ValueDecl *member = choice.getDecl();
|
|
|
|
bool isSuper = base->isSuperExpr();
|
|
|
|
// The formal type of the 'self' value for the call.
|
|
Type baseTy = cs.getType(base)->getRValueType();
|
|
|
|
// Figure out the actual base type, and whether we have an instance of
|
|
// that type or its metatype.
|
|
bool baseIsInstance = true;
|
|
bool isExistentialMetatype = false;
|
|
if (auto baseMeta = baseTy->getAs<AnyMetatypeType>()) {
|
|
baseIsInstance = false;
|
|
isExistentialMetatype = baseMeta->is<ExistentialMetatypeType>();
|
|
baseTy = baseMeta->getInstanceType();
|
|
if (auto existential = baseTy->getAs<ExistentialType>())
|
|
baseTy = existential->getConstraintType();
|
|
|
|
// A valid reference to a static member (computed property or a method)
|
|
// declared on a protocol is only possible if result type conforms to
|
|
// that protocol, otherwise it would be impossible to find a witness to
|
|
// use.
|
|
// Such means that (for valid references) base expression here could be
|
|
// adjusted to point to a type conforming to a protocol as-if reference
|
|
// has originated directly from it e.g.
|
|
//
|
|
// \code
|
|
// protocol P {}
|
|
// struct S : P {}
|
|
//
|
|
// extension P {
|
|
// static var foo: S { S() }
|
|
// }
|
|
//
|
|
// _ = P.foo
|
|
// \endcode
|
|
//
|
|
// Here `P.foo` would be replaced with `S.foo`
|
|
if (!isExistentialMetatype && baseTy->isConstraintType() &&
|
|
member->isStatic()) {
|
|
auto selfParam =
|
|
overload.adjustedOpenedFullType->castTo<FunctionType>()->getParams()[0];
|
|
|
|
Type baseTy =
|
|
simplifyType(selfParam.getPlainType())->getMetatypeInstanceType();
|
|
|
|
base = TypeExpr::createImplicitHack(base->getLoc(), baseTy, ctx);
|
|
cs.cacheType(base);
|
|
}
|
|
}
|
|
|
|
// Build a member reference.
|
|
auto memberRef = resolveConcreteDeclRef(member, memberLocator);
|
|
|
|
// If our member reference is a value generic, then the resulting
|
|
// expression is the type value one to access the underlying parameter's
|
|
// value.
|
|
//
|
|
// This can occur in code that does something like: 'type(of: x).a' where
|
|
// 'a' is the static value generic member.
|
|
if (auto gp = dyn_cast<GenericTypeParamDecl>(member)) {
|
|
ASSERT(gp->isValue());
|
|
|
|
auto refType = adjustedOpenedType;
|
|
auto ref = TypeValueExpr::createForDecl(memberLoc, gp, dc);
|
|
cs.setType(ref, refType);
|
|
|
|
auto gpTy = gp->getDeclaredInterfaceType();
|
|
auto subs = baseTy->getContextSubstitutionMap();
|
|
ref->setParamType(gpTy.subst(subs));
|
|
|
|
auto result = new (ctx) DotSyntaxBaseIgnoredExpr(base, dotLoc, ref,
|
|
refType);
|
|
cs.setType(result, refType);
|
|
return result;
|
|
}
|
|
|
|
// If we're referring to a member type, it's just a type
|
|
// reference.
|
|
if (auto *TD = dyn_cast<TypeDecl>(member)) {
|
|
Type refType = adjustedOpenedType;
|
|
auto ref = TypeExpr::createForDecl(memberLoc, TD, dc);
|
|
cs.setType(ref, refType);
|
|
auto *result = new (ctx) DotSyntaxBaseIgnoredExpr(
|
|
base, dotLoc, ref, refType);
|
|
cs.setType(result, refType);
|
|
return result;
|
|
}
|
|
|
|
Type refTy = simplifyType(overload.openedFullType);
|
|
Type adjustedRefTy = simplifyType(overload.adjustedOpenedFullType);
|
|
|
|
// If we're referring to the member of a module, it's just a simple
|
|
// reference.
|
|
if (baseTy->is<ModuleType>()) {
|
|
assert(semantics == AccessSemantics::Ordinary &&
|
|
"Direct property access doesn't make sense for this");
|
|
auto *dre = new (ctx) DeclRefExpr(memberRef, memberLoc, Implicit);
|
|
cs.setType(dre, refTy);
|
|
dre->setFunctionRefInfo(choice.getFunctionRefInfo());
|
|
Expr *ref = cs.cacheType(new (ctx) DotSyntaxBaseIgnoredExpr(
|
|
base, dotLoc, dre, refTy));
|
|
|
|
ref = adjustTypeForDeclReference(ref, refTy, adjustedRefTy,
|
|
locator);
|
|
return forceUnwrapIfExpected(ref, memberLocator);
|
|
}
|
|
|
|
const bool isUnboundInstanceMember =
|
|
(!baseIsInstance && member->isInstanceMember());
|
|
const bool needsCurryThunk =
|
|
shouldBuildCurryThunk(choice, baseIsInstance);
|
|
|
|
// The formal type of the 'self' value for the member's declaration.
|
|
Type containerTy = getBaseType(refTy->castTo<FunctionType>());
|
|
|
|
// If we have an opened existential, selfTy and baseTy will both be
|
|
// the same opened existential type.
|
|
Type selfTy = containerTy;
|
|
|
|
// If we opened up an existential when referencing this member, update
|
|
// the base accordingly.
|
|
Type baseOpenedTy = baseTy;
|
|
bool openedExistential = false;
|
|
|
|
auto knownOpened = solution.OpenedExistentialTypes.find(
|
|
getConstraintSystem().getConstraintLocator(
|
|
memberLocator));
|
|
if (knownOpened != solution.OpenedExistentialTypes.end()) {
|
|
baseOpenedTy = knownOpened->second;
|
|
|
|
// Determine if we're going to have an OpenExistentialExpr around
|
|
// this member reference.
|
|
//
|
|
// For an unbound reference to a method, always open the existential
|
|
// inside the curry thunk, because we won't have a 'self' value until
|
|
// the curry thunk is applied.
|
|
//
|
|
// For a partial application of a protocol method, open the existential
|
|
// inside the curry thunk as well. This reduces abstraction and
|
|
// post-factum function type conversions, and results in better SILGen.
|
|
//
|
|
// For a partial application of a class instance method, however, we
|
|
// always want the thunk to accept a class to avoid potential
|
|
// abstraction, so the existential base must be opened eagerly in order
|
|
// to be upcast to the appropriate class reference type before it is
|
|
// passed to the thunk.
|
|
if (!needsCurryThunk ||
|
|
(!member->getDeclContext()->getSelfProtocolDecl() &&
|
|
baseIsInstance && member->isInstanceMember())) {
|
|
// Open the existential before performing the member reference.
|
|
base = openExistentialReference(base, knownOpened->second, member,
|
|
memberLoc.getBaseNameLoc());
|
|
baseTy = baseOpenedTy;
|
|
selfTy = baseTy;
|
|
openedExistential = true;
|
|
} else {
|
|
// Erase opened existentials from the type of the thunk; we're
|
|
// going to open the existential inside the thunk's body.
|
|
containerTy = typeEraseOpenedArchetypesFromEnvironment(
|
|
containerTy, knownOpened->second->getGenericEnvironment());
|
|
selfTy = containerTy;
|
|
}
|
|
}
|
|
|
|
// References to properties with accessors and storage usually go
|
|
// through the accessors, but sometimes are direct.
|
|
if (auto *VD = dyn_cast<VarDecl>(member)) {
|
|
if (semantics == AccessSemantics::Ordinary)
|
|
semantics = getImplicitMemberReferenceAccessSemantics(base, VD, dc);
|
|
}
|
|
|
|
auto isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic;
|
|
if (baseIsInstance) {
|
|
// Convert the base to the appropriate container type, turning it
|
|
// into an lvalue if required.
|
|
|
|
// If the base is already an lvalue with the right base type, we can
|
|
// pass it as an inout qualified type.
|
|
auto selfParamTy = isDynamic ? selfTy : containerTy;
|
|
|
|
// If type equality check fails we need to check whether the types
|
|
// are the same with deep equality restriction since `any Sendable`
|
|
// to `Any` conversion is now supported in generic argument positions
|
|
// of @preconcurrency declarations. i.e. referencing a member on
|
|
// `[any Sendable]` if member declared in an extension that expects
|
|
// `Element` to be equal to `Any`.
|
|
if (selfTy->isEqual(baseTy) ||
|
|
solution.getConversionRestriction(baseTy->getCanonicalType(),
|
|
selfTy->getCanonicalType()) ==
|
|
ConversionRestrictionKind::DeepEquality)
|
|
if (cs.getType(base)->is<LValueType>())
|
|
selfParamTy = InOutType::get(selfTy);
|
|
|
|
base = coerceSelfArgumentToType(
|
|
base, selfParamTy, member,
|
|
locator.withPathElement(ConstraintLocator::MemberRefBase));
|
|
} else {
|
|
// The base of an unbound reference is unused, and thus a conversion
|
|
// is not necessary.
|
|
if (!isUnboundInstanceMember) {
|
|
if (!isExistentialMetatype || openedExistential) {
|
|
// Convert the base to an rvalue of the appropriate metatype.
|
|
base = coerceToType(
|
|
base, MetatypeType::get(isDynamic ? selfTy : containerTy),
|
|
locator.withPathElement(ConstraintLocator::MemberRefBase));
|
|
}
|
|
}
|
|
|
|
if (!base)
|
|
return nullptr;
|
|
|
|
base = cs.coerceToRValue(base);
|
|
}
|
|
assert(base && "Unable to convert base?");
|
|
|
|
// This is the type of the DeclRefExpr, where DynamicSelfType has
|
|
// been replaced with the static Self type of the member, ie the
|
|
// base class it is declared in.
|
|
Type refTySelf = refTy, adjustedRefTySelf = adjustedRefTy;
|
|
|
|
// Now, deal with DynamicSelfType.
|
|
if (overload.openedFullType->hasDynamicSelfType()) {
|
|
// We look at the original opened type with unsimplified type
|
|
// variables, because we only want to erase DynamicSelfType
|
|
// that appears in the original type of the member, and not
|
|
// one introduced by substitution.
|
|
refTySelf = simplifyType(
|
|
overload.openedFullType->eraseDynamicSelfType());
|
|
adjustedRefTySelf = simplifyType(
|
|
overload.adjustedOpenedFullType->eraseDynamicSelfType());
|
|
|
|
// Now replace DynamicSelfType with the actual base type
|
|
// of the call.
|
|
auto replacementTy = getDynamicSelfReplacementType(
|
|
baseOpenedTy, member, memberLocator.getBaseLocator());
|
|
refTy = simplifyType(
|
|
overload.openedFullType
|
|
->replaceDynamicSelfType(replacementTy));
|
|
adjustedRefTy = simplifyType(
|
|
overload.adjustedOpenedFullType
|
|
->replaceDynamicSelfType(replacementTy));
|
|
}
|
|
|
|
// Handle dynamic references.
|
|
if (!needsCurryThunk &&
|
|
(isDynamic || member->getAttrs().hasAttribute<OptionalAttr>())) {
|
|
return buildDynamicMemberRef(base, baseTy, dotLoc, memberRef,
|
|
openedType, adjustedOpenedType,
|
|
memberLoc, locator, memberLocator,
|
|
openedExistential, Implicit);
|
|
}
|
|
|
|
// For properties, build member references.
|
|
if (isa<VarDecl>(member)) {
|
|
return buildVarMemberRef(base, dotLoc, baseIsInstance,
|
|
memberRef, memberLoc, containerTy,
|
|
refTy, refTySelf, adjustedRefTy, adjustedRefTySelf,
|
|
semantics, locator, memberLocator,
|
|
isSuper, isUnboundInstanceMember, Implicit);
|
|
}
|
|
|
|
ASSERT(isa<AbstractFunctionDecl>(member) ||
|
|
isa<EnumElementDecl>(member));
|
|
|
|
// Handle all other references.
|
|
auto declRefExpr = new (ctx) DeclRefExpr(memberRef, memberLoc,
|
|
Implicit, semantics);
|
|
declRefExpr->setFunctionRefInfo(choice.getFunctionRefInfo());
|
|
declRefExpr->setType(refTySelf);
|
|
cs.setType(declRefExpr, refTySelf);
|
|
Expr *ref = declRefExpr;
|
|
|
|
ref = adjustTypeForDeclReference(ref, refTySelf, adjustedRefTySelf, locator);
|
|
|
|
// A partial application thunk consists of two nested closures:
|
|
//
|
|
// { self in { args... in self.method(args...) } }
|
|
//
|
|
// If the reference has an applied 'self', eg 'let fn = foo.method',
|
|
// the outermost closure is wrapped inside a single ApplyExpr:
|
|
//
|
|
// { self in { args... in self.method(args...) } }(foo)
|
|
//
|
|
// This is done instead of just hoisting the expression 'foo' up
|
|
// into the closure, which would change evaluation order.
|
|
//
|
|
// However, for a super method reference, eg, 'let fn = super.foo',
|
|
// the base expression is always a SuperRefExpr, possibly wrapped
|
|
// by an upcast. Since SILGen expects super method calls to have a
|
|
// very specific shape, we only emit a single closure here and
|
|
// capture the original SuperRefExpr, since its evaluation does not
|
|
// have side effects, instead of abstracting out a 'self' parameter.
|
|
if (isUnboundInstanceMember) {
|
|
if (needsCurryThunk) {
|
|
// For an unbound reference to a method, all conversions, including
|
|
// dynamic 'Self' handling, are done within the thunk to support
|
|
// the edge case of an unbound reference to a 'Self'-returning class
|
|
// method on a protocol metatype. The result of calling the method
|
|
// must be downcast to the opened archetype before being erased to the
|
|
// subclass existential to cope with the expectations placed
|
|
// on 'CovariantReturnConversionExpr'.
|
|
auto *curryThunkTy = adjustedOpenedType->castTo<FunctionType>();
|
|
auto *curryRefTy = adjustedRefTy->castTo<FunctionType>();
|
|
|
|
// Replace the DeclRefExpr with a closure expression which SILGen
|
|
// knows how to emit.
|
|
ref = buildDoubleCurryThunk(declRefExpr, member,
|
|
curryThunkTy, curryRefTy,
|
|
memberLocator, memberLoc,
|
|
isDynamic);
|
|
}
|
|
|
|
ref = adjustTypeForDeclReference(
|
|
ref, cs.getType(ref), adjustedOpenedType,
|
|
locator);
|
|
|
|
// Reference to an unbound instance method.
|
|
Expr *result = new (ctx) DotSyntaxBaseIgnoredExpr(base, dotLoc,
|
|
ref,
|
|
cs.getType(ref));
|
|
cs.cacheType(result);
|
|
closeExistentials(result, locator, /*force=*/openedExistential);
|
|
return forceUnwrapIfExpected(result, memberLocator);
|
|
|
|
} else if (needsCurryThunk && isSuper) {
|
|
ref = buildSingleCurryThunk(
|
|
base, declRefExpr, cast<AbstractFunctionDecl>(member),
|
|
adjustedOpenedType->castTo<FunctionType>(),
|
|
adjustedRefTy->castTo<FunctionType>()
|
|
->getResult()->castTo<FunctionType>(),
|
|
memberLocator);
|
|
|
|
// Handle DynamicSelfType.
|
|
if (!adjustedRefTy->isEqual(adjustedRefTySelf)) {
|
|
auto conversionTy = adjustedRefTy->castTo<FunctionType>()->getResult();
|
|
ref = cs.cacheType(new (ctx) CovariantFunctionConversionExpr(
|
|
ref, conversionTy));
|
|
}
|
|
|
|
// The thunk that is built for a 'super' method reference does not
|
|
// require application.
|
|
return forceUnwrapIfExpected(ref, memberLocator);
|
|
|
|
} else if (needsCurryThunk) {
|
|
// Another case where we want to build a single closure is when
|
|
// we have a partial application of a static member. It is better
|
|
// to either push the base reference down into the closure (if it's
|
|
// just a literal type reference, in which case there are no order of
|
|
// operation concerns with capturing vs. evaluating it in the closure),
|
|
// or to evaluate the base as a capture and hand it down via the
|
|
// capture list.
|
|
if (isa<ConstructorDecl>(member) || member->isStatic()) {
|
|
return buildStaticCurryThunk(
|
|
base, declRefExpr, cast<AbstractFunctionDecl>(member),
|
|
adjustedOpenedType->castTo<FunctionType>(),
|
|
adjustedRefTy->castTo<FunctionType>()
|
|
->getResult()->castTo<FunctionType>(),
|
|
locator, memberLocator, openedExistential);
|
|
}
|
|
|
|
auto *curryThunkTy = adjustedRefTySelf->castTo<FunctionType>();
|
|
auto *curryRefTy = curryThunkTy;
|
|
|
|
// Check if we need to open an existential stored inside 'self'.
|
|
if (knownOpened != solution.OpenedExistentialTypes.end()) {
|
|
curryThunkTy =
|
|
typeEraseOpenedArchetypesFromEnvironment(
|
|
curryThunkTy, knownOpened->second->getGenericEnvironment())
|
|
->castTo<FunctionType>();
|
|
}
|
|
|
|
// Replace the DeclRefExpr with a closure expression which SILGen
|
|
// knows how to emit.
|
|
ref = buildDoubleCurryThunk(declRefExpr, member,
|
|
curryThunkTy, curryRefTy,
|
|
memberLocator, memberLoc,
|
|
isDynamic);
|
|
|
|
// Fall through, but with 'ref' now an AutoClosureExpr instead of
|
|
// a DeclRefExpr.
|
|
}
|
|
|
|
// If the member is a method with a dynamic 'Self' result type, wrap an
|
|
// implicit function type conversion around the resulting expression,
|
|
// with the destination type having 'Self' swapped for the appropriate
|
|
// replacement type -- usually the base object type.
|
|
//
|
|
// Note: For unbound references this is handled inside the thunk.
|
|
if (member->getDeclContext()->getSelfClassDecl()) {
|
|
if (!adjustedRefTy->isEqual(adjustedRefTySelf)) {
|
|
ref = cs.cacheType(new (ctx) CovariantFunctionConversionExpr(
|
|
ref, adjustedRefTy));
|
|
}
|
|
}
|
|
|
|
ApplyExpr *apply;
|
|
if (isa<ConstructorDecl>(member)) {
|
|
// FIXME: Provide type annotation.
|
|
ref = forceUnwrapIfExpected(ref, memberLocator);
|
|
apply = ConstructorRefCallExpr::create(ctx, ref, base);
|
|
} else {
|
|
assert((!baseIsInstance || member->isInstanceMember()) &&
|
|
"can't call a static method on an instance");
|
|
ref = forceUnwrapIfExpected(ref, memberLocator);
|
|
apply = DotSyntaxCallExpr::create(ctx, ref, dotLoc, Argument::unlabeled(base));
|
|
if (Implicit) {
|
|
apply->setImplicit();
|
|
}
|
|
}
|
|
|
|
return finishApply(apply, adjustedOpenedType, locator, memberLocator);
|
|
}
|
|
|
|
/// Convert the given literal expression via a protocol pair.
|
|
///
|
|
/// This routine handles the two-step literal conversion process used
|
|
/// by integer, float, character, extended grapheme cluster, and string
|
|
/// literals. The first step uses \c builtinProtocol while the second
|
|
/// step uses \c protocol.
|
|
///
|
|
/// \param literal The literal expression.
|
|
///
|
|
/// \param type The literal type. This type conforms to \c protocol,
|
|
/// and may also conform to \c builtinProtocol.
|
|
///
|
|
/// \param protocol The protocol that describes the literal requirement.
|
|
///
|
|
/// \param literalType The name of the associated type in \c protocol that
|
|
/// describes the argument type of the conversion function (\c
|
|
/// literalFuncName).
|
|
///
|
|
/// \param literalFuncName The name of the conversion function requirement
|
|
/// in \c protocol.
|
|
///
|
|
/// \param builtinProtocol The "builtin" form of the protocol, which
|
|
/// always takes builtin types and can only be properly implemented
|
|
/// by standard library types. If \c type does not conform to this
|
|
/// protocol, it's literal type will.
|
|
///
|
|
/// \param builtinLiteralFuncName The name of the conversion function
|
|
/// requirement in \c builtinProtocol.
|
|
///
|
|
/// \param brokenProtocolDiag The diagnostic to emit if the protocol
|
|
/// is broken.
|
|
///
|
|
/// \param brokenBuiltinProtocolDiag The diagnostic to emit if the builtin
|
|
/// protocol is broken.
|
|
///
|
|
/// \returns the converted literal expression.
|
|
Expr *convertLiteralInPlace(LiteralExpr *literal, Type type,
|
|
ProtocolDecl *protocol, Identifier literalType,
|
|
DeclName literalFuncName,
|
|
ProtocolDecl *builtinProtocol,
|
|
DeclName builtinLiteralFuncName,
|
|
Diag<> brokenProtocolDiag,
|
|
Diag<> brokenBuiltinProtocolDiag);
|
|
|
|
/// Finish a function application by performing the appropriate
|
|
/// conversions on the function and argument expressions and setting
|
|
/// the resulting type.
|
|
///
|
|
/// \param apply The function application to finish type-checking, which
|
|
/// may be a newly-built expression.
|
|
///
|
|
/// \param openedType The "opened" type this expression had during
|
|
/// type checking, which will be used to specialize the resulting,
|
|
/// type-checked expression appropriately.
|
|
///
|
|
/// \param locator The locator for the original expression.
|
|
///
|
|
/// \param calleeLocator The locator that identifies the apply's callee.
|
|
Expr *finishApply(ApplyExpr *apply, Type openedType,
|
|
ConstraintLocatorBuilder locator,
|
|
ConstraintLocatorBuilder calleeLocator);
|
|
|
|
/// Build the function and argument list for a `@dynamicCallable`
|
|
/// application.
|
|
std::pair</*fn*/ Expr *, ArgumentList *>
|
|
buildDynamicCallable(ApplyExpr *apply, SelectedOverload selected,
|
|
FuncDecl *method, AnyFunctionType *methodType,
|
|
ConstraintLocatorBuilder applyFunctionLoc);
|
|
|
|
private:
|
|
/// Simplify the given type by substituting all occurrences of
|
|
/// type variables for their fixed types.
|
|
Type simplifyType(Type type) {
|
|
return solution.simplifyType(type);
|
|
}
|
|
|
|
public:
|
|
/// Coerce the given expression to the given type.
|
|
///
|
|
/// This operation cannot fail.
|
|
///
|
|
/// \param expr The expression to coerce.
|
|
/// \param toType The type to coerce the expression to.
|
|
/// \param locator Locator used to describe where in this expression we are.
|
|
///
|
|
/// \returns the coerced expression, which will have type \c ToType.
|
|
Expr *coerceToType(Expr *expr, Type toType,
|
|
ConstraintLocatorBuilder locator);
|
|
|
|
/// Coerce the arguments in the provided argument list to their matching
|
|
/// parameter types.
|
|
///
|
|
/// This operation cannot fail.
|
|
///
|
|
/// \param args The argument list.
|
|
/// \param funcType The function type.
|
|
/// \param callee The callee for the function being applied.
|
|
/// \param apply The ApplyExpr that forms the call.
|
|
/// \param locator Locator used to describe where in this expression we are.
|
|
///
|
|
/// \returns The resulting ArgumentList.
|
|
ArgumentList *coerceCallArguments(
|
|
ArgumentList *args, AnyFunctionType *funcType, ConcreteDeclRef callee,
|
|
ApplyExpr *apply, ConstraintLocatorBuilder locator,
|
|
ArrayRef<AppliedPropertyWrapper> appliedPropertyWrappers);
|
|
|
|
/// Coerce the given 'self' argument (e.g., for the base of a
|
|
/// member expression) to the given type.
|
|
///
|
|
/// \param expr The expression to coerce.
|
|
///
|
|
/// \param baseTy The base type
|
|
///
|
|
/// \param member The member being accessed.
|
|
///
|
|
/// \param locator Locator used to describe where in this expression we are.
|
|
Expr *coerceSelfArgumentToType(Expr *expr,
|
|
Type baseTy, ValueDecl *member,
|
|
ConstraintLocatorBuilder locator);
|
|
|
|
private:
|
|
/// Build a new subscript.
|
|
///
|
|
/// \param base The base of the subscript.
|
|
/// \param args The argument list of the subscript.
|
|
/// \param locator The locator used to refer to the subscript.
|
|
/// \param isImplicit Whether this is an implicit subscript.
|
|
Expr *buildSubscript(Expr *base, ArgumentList *args,
|
|
ConstraintLocatorBuilder locator,
|
|
ConstraintLocatorBuilder memberLocator,
|
|
bool isImplicit, AccessSemantics semantics,
|
|
const SelectedOverload &selected) {
|
|
// Build the new subscript.
|
|
auto newSubscript = buildSubscriptHelper(base, args, selected,
|
|
locator, isImplicit, semantics);
|
|
return forceUnwrapIfExpected(newSubscript, memberLocator,
|
|
IUOReferenceKind::ReturnValue);
|
|
}
|
|
|
|
Expr *buildSubscriptHelper(Expr *base, ArgumentList *args,
|
|
const SelectedOverload &selected,
|
|
ConstraintLocatorBuilder locator,
|
|
bool isImplicit, AccessSemantics semantics) {
|
|
auto choice = selected.choice;
|
|
|
|
// Apply a key path if we have one.
|
|
if (choice.getKind() == OverloadChoiceKind::KeyPathApplication) {
|
|
auto applicationTy =
|
|
simplifyType(selected.adjustedOpenedType)->castTo<FunctionType>();
|
|
|
|
// The index argument should be (keyPath: KeyPath<Root, Value>).
|
|
// Dig the key path expression out of the arguments.
|
|
auto *indexKP = args->getUnaryExpr();
|
|
assert(indexKP);
|
|
indexKP = cs.coerceToRValue(indexKP);
|
|
auto keyPathExprTy = cs.getType(indexKP);
|
|
auto keyPathTy = applicationTy->getParams().front().getOldType();
|
|
|
|
Type valueTy;
|
|
Type baseTy;
|
|
bool resultIsLValue;
|
|
|
|
if (auto nom = keyPathTy->getAs<NominalType>()) {
|
|
// AnyKeyPath is <T> rvalue T -> rvalue Any?
|
|
if (nom->isAnyKeyPath()) {
|
|
valueTy = ctx.getAnyExistentialType();
|
|
valueTy = OptionalType::get(valueTy);
|
|
resultIsLValue = false;
|
|
base = cs.coerceToRValue(base);
|
|
baseTy = cs.getType(base);
|
|
// We don't really want to attempt AnyKeyPath application
|
|
// if we know a more specific key path type is being applied.
|
|
if (!keyPathTy->isEqual(keyPathExprTy)) {
|
|
ctx.Diags
|
|
.diagnose(base->getLoc(),
|
|
diag::expr_smart_keypath_application_type_mismatch,
|
|
keyPathExprTy, baseTy)
|
|
.highlight(args->getSourceRange());
|
|
}
|
|
} else {
|
|
llvm_unreachable("unknown key path class!");
|
|
}
|
|
} else {
|
|
auto keyPathBGT = keyPathTy->castTo<BoundGenericType>();
|
|
baseTy = keyPathBGT->getGenericArgs()[0];
|
|
|
|
// Coerce the index to the key path's type
|
|
indexKP = coerceToType(indexKP, keyPathTy, locator);
|
|
|
|
// Coerce the base to the key path's expected base type.
|
|
if (!baseTy->isEqual(cs.getType(base)->getRValueType()))
|
|
base = coerceToType(base, baseTy, locator);
|
|
|
|
if (keyPathBGT->isPartialKeyPath()) {
|
|
// PartialKeyPath<T> is rvalue T -> rvalue Any
|
|
valueTy = ctx.getAnyExistentialType();
|
|
resultIsLValue = false;
|
|
base = cs.coerceToRValue(base);
|
|
} else {
|
|
// *KeyPath<T, U> is T -> U, with rvalueness based on mutability
|
|
// of base and keypath
|
|
valueTy = keyPathBGT->getGenericArgs()[1];
|
|
|
|
// The result may be an lvalue based on the base and key path kind.
|
|
if (keyPathBGT->isKeyPath()) {
|
|
resultIsLValue = false;
|
|
base = cs.coerceToRValue(base);
|
|
} else if (keyPathBGT->isWritableKeyPath()) {
|
|
resultIsLValue = cs.getType(base)->hasLValueType();
|
|
} else if (keyPathBGT->isReferenceWritableKeyPath()) {
|
|
resultIsLValue = true;
|
|
base = cs.coerceToRValue(base);
|
|
} else {
|
|
llvm_unreachable("unknown key path class!");
|
|
}
|
|
}
|
|
}
|
|
if (resultIsLValue)
|
|
valueTy = LValueType::get(valueTy);
|
|
|
|
auto keyPathAp = new (ctx) KeyPathApplicationExpr(
|
|
base, args->getStartLoc(), indexKP, args->getEndLoc(), valueTy,
|
|
base->isImplicit() && args->isImplicit());
|
|
cs.setType(keyPathAp, valueTy);
|
|
return keyPathAp;
|
|
}
|
|
|
|
auto subscript = cast<SubscriptDecl>(choice.getDecl());
|
|
|
|
auto baseTy = cs.getType(base)->getRValueType();
|
|
|
|
bool baseIsInstance = true;
|
|
if (auto baseMeta = baseTy->getAs<AnyMetatypeType>()) {
|
|
baseIsInstance = false;
|
|
baseTy = baseMeta->getInstanceType();
|
|
}
|
|
|
|
// Check whether the base is 'super'.
|
|
bool isSuper = base->isSuperExpr();
|
|
|
|
// If we opened up an existential when performing the subscript, open
|
|
// the base accordingly.
|
|
auto memberLoc = cs.getCalleeLocator(cs.getConstraintLocator(locator));
|
|
auto knownOpened = solution.OpenedExistentialTypes.find(memberLoc);
|
|
if (knownOpened != solution.OpenedExistentialTypes.end()) {
|
|
base = openExistentialReference(base, knownOpened->second, subscript,
|
|
args->getLoc());
|
|
baseTy = knownOpened->second;
|
|
}
|
|
|
|
// Compute the concrete reference to the subscript.
|
|
auto subscriptRef = resolveConcreteDeclRef(subscript, memberLoc);
|
|
|
|
// Coerce the index argument.
|
|
auto openedFullFnType = simplifyType(selected.adjustedOpenedFullType)
|
|
->castTo<FunctionType>();
|
|
auto fullSubscriptTy = openedFullFnType->getResult()
|
|
->castTo<FunctionType>();
|
|
auto appliedWrappers =
|
|
solution.getAppliedPropertyWrappers(memberLoc->getAnchor());
|
|
args = coerceCallArguments(
|
|
args, fullSubscriptTy, subscriptRef, nullptr,
|
|
locator.withPathElement(ConstraintLocator::ApplyArgument),
|
|
appliedWrappers);
|
|
if (!args)
|
|
return nullptr;
|
|
|
|
// Handle dynamic lookup.
|
|
if (choice.getKind() == OverloadChoiceKind::DeclViaDynamic ||
|
|
subscript->getAttrs().hasAttribute<OptionalAttr>()) {
|
|
base = coerceSelfArgumentToType(base, baseTy, subscript, locator);
|
|
if (!base)
|
|
return nullptr;
|
|
|
|
// TODO: diagnose if semantics != AccessSemantics::Ordinary?
|
|
auto subscriptExpr = DynamicSubscriptExpr::create(
|
|
ctx, base, args, subscriptRef, isImplicit);
|
|
auto resultTy = simplifyType(selected.adjustedOpenedType)
|
|
->castTo<FunctionType>()
|
|
->getResult();
|
|
assert(!selected.adjustedOpenedFullType->hasOpenedExistential()
|
|
&& "open existential archetype in AnyObject subscript type?");
|
|
cs.setType(subscriptExpr, resultTy);
|
|
Expr *result = subscriptExpr;
|
|
closeExistentials(result, locator);
|
|
return result;
|
|
}
|
|
|
|
// Convert the base.
|
|
auto openedBaseType =
|
|
getBaseType(openedFullFnType, /*wantsRValue*/ false);
|
|
auto containerTy = solution.simplifyType(openedBaseType);
|
|
|
|
if (baseIsInstance) {
|
|
base = coerceSelfArgumentToType(
|
|
base, containerTy, subscript,
|
|
locator.withPathElement(ConstraintLocator::MemberRefBase));
|
|
} else {
|
|
base = coerceToType(base,
|
|
MetatypeType::get(containerTy),
|
|
locator.withPathElement(
|
|
ConstraintLocator::MemberRefBase));
|
|
|
|
if (!base)
|
|
return nullptr;
|
|
|
|
base = cs.coerceToRValue(base);
|
|
}
|
|
if (!base)
|
|
return nullptr;
|
|
|
|
bool hasDynamicSelf = fullSubscriptTy->hasDynamicSelfType();
|
|
|
|
// Form the subscript expression.
|
|
auto subscriptExpr = SubscriptExpr::create(
|
|
ctx, base, args, subscriptRef, isImplicit, semantics);
|
|
subscriptExpr->setIsSuper(isSuper);
|
|
|
|
if (!hasDynamicSelf) {
|
|
cs.setType(subscriptExpr, fullSubscriptTy->getResult());
|
|
} else {
|
|
cs.setType(subscriptExpr,
|
|
fullSubscriptTy->getResult()
|
|
->replaceDynamicSelfType(containerTy));
|
|
}
|
|
|
|
Expr *result = subscriptExpr;
|
|
closeExistentials(result, locator);
|
|
|
|
// If the element is of dynamic 'Self' type, wrap an implicit conversion
|
|
// around the resulting expression, with the destination type having
|
|
// 'Self' swapped for the appropriate replacement type -- usually the
|
|
// base object type.
|
|
if (hasDynamicSelf) {
|
|
const auto conversionTy = simplifyType(
|
|
selected.adjustedOpenedType->castTo<FunctionType>()->getResult());
|
|
|
|
if (!containerTy->isEqual(conversionTy)) {
|
|
result = cs.cacheType(
|
|
new (ctx) CovariantReturnConversionExpr(result, conversionTy));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// Build a new reference to another constructor.
|
|
Expr *buildOtherConstructorRef(Type openedFullType,
|
|
ConcreteDeclRef ref, Expr *base,
|
|
DeclNameLoc loc,
|
|
ConstraintLocatorBuilder locator,
|
|
bool implicit) {
|
|
// The constructor was opened with the allocating type, not the
|
|
// initializer type. Map the former into the latter.
|
|
auto getOpenedInitializerType = [&](Type ty) -> FunctionType * {
|
|
auto *resultTy = solution.simplifyType(ty)->castTo<FunctionType>();
|
|
auto selfTy = getBaseType(resultTy);
|
|
|
|
ParameterTypeFlags flags;
|
|
if (!selfTy->hasReferenceSemantics())
|
|
flags = flags.withInOut(true);
|
|
|
|
auto selfParam = AnyFunctionType::Param(selfTy, Identifier(), flags);
|
|
return FunctionType::get({selfParam},
|
|
resultTy->getResult(),
|
|
resultTy->getExtInfo());
|
|
};
|
|
|
|
auto *resultTySelf = getOpenedInitializerType(
|
|
openedFullType->eraseDynamicSelfType());
|
|
|
|
// Build the constructor reference.
|
|
Expr *ctorRef = cs.cacheType(
|
|
new (ctx) OtherConstructorDeclRefExpr(ref, loc, implicit, resultTySelf));
|
|
|
|
auto *resultTy = getOpenedInitializerType(
|
|
openedFullType->replaceDynamicSelfType(
|
|
cs.getType(base)->getWithoutSpecifierType()));
|
|
|
|
// Wrap in covariant `Self` return if needed.
|
|
if (!resultTy->isEqual(resultTySelf)) {
|
|
ASSERT(ref.getDecl()->getDeclContext()->getSelfClassDecl());
|
|
ctorRef = cs.cacheType(
|
|
new (ctx) CovariantFunctionConversionExpr(ctorRef, resultTy));
|
|
}
|
|
|
|
return ctorRef;
|
|
}
|
|
|
|
/// Build an implicit argument for keypath based dynamic lookup,
|
|
/// which consists of KeyPath expression and a single component.
|
|
///
|
|
/// \param paramType The type of the keypath subscript parameter
|
|
/// this argument is passed to.
|
|
/// \param dotLoc The location of the '.' preceding member name.
|
|
/// \param memberLoc The locator to be associated with new argument.
|
|
Expr *buildKeyPathDynamicMemberArgExpr(Type paramType, SourceLoc dotLoc,
|
|
ConstraintLocator *memberLoc) {
|
|
using Component = KeyPathExpr::Component;
|
|
auto *anchor = getAsExpr(memberLoc->getAnchor());
|
|
|
|
auto makeKeyPath = [&](ArrayRef<Component> components) -> Expr * {
|
|
Type keyPathTy = paramType;
|
|
|
|
// If parameter of a dynamic member lookup is `& Sendable` type
|
|
// we need to check key path captures to determine whether the
|
|
// argument could be `& Sendable` as well or not.
|
|
if (paramType->isExistentialType() && paramType->isSendableType()) {
|
|
auto allCapturesAreSendable = [&](const Component &component) {
|
|
auto *argList = component.getArgs();
|
|
if (!argList)
|
|
return true;
|
|
|
|
return llvm::all_of(*argList, [&](const auto &arg) {
|
|
return solution.getResolvedType(arg.getExpr())->isSendableType();
|
|
});
|
|
};
|
|
|
|
if (!llvm::all_of(components, allCapturesAreSendable))
|
|
keyPathTy = paramType->getSuperclass();
|
|
}
|
|
|
|
auto *kp = KeyPathExpr::createImplicit(ctx, /*backslashLoc*/ dotLoc,
|
|
components, anchor->getEndLoc());
|
|
kp->setType(keyPathTy);
|
|
cs.cacheExprTypes(kp);
|
|
|
|
// See whether there's an equivalent ObjC key path string we can produce
|
|
// for interop purposes.
|
|
checkAndSetObjCKeyPathString(kp);
|
|
return kp;
|
|
};
|
|
|
|
Type keyPathTy = paramType;
|
|
if (auto *existential = keyPathTy->getAs<ExistentialType>()) {
|
|
keyPathTy = existential->getSuperclass();
|
|
assert(isKnownKeyPathType(keyPathTy));
|
|
}
|
|
|
|
SmallVector<Component, 2> components;
|
|
auto *componentLoc = cs.getConstraintLocator(
|
|
memberLoc,
|
|
LocatorPathElt::KeyPathDynamicMember(keyPathTy->getAnyNominal()));
|
|
auto overload = solution.getOverloadChoice(componentLoc);
|
|
|
|
// Looks like there is a chain of implicit `subscript(dynamicMember:)`
|
|
// calls necessary to resolve a member reference.
|
|
switch (overload.choice.getKind()) {
|
|
case OverloadChoiceKind::DynamicMemberLookup:
|
|
case OverloadChoiceKind::KeyPathDynamicMemberLookup: {
|
|
buildKeyPathSubscriptComponent(overload, dotLoc, /*args=*/nullptr,
|
|
componentLoc, components);
|
|
return makeKeyPath(components);
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (auto *KPE = dyn_cast<KeyPathExpr>(anchor)) {
|
|
// Looks like keypath dynamic member lookup was used inside
|
|
// of a keypath expression e.g. `\Lens<[Int]>.count` where
|
|
// `count` is referenced using dynamic lookup.
|
|
auto kpElt = memberLoc->findFirst<LocatorPathElt::KeyPathComponent>();
|
|
assert(kpElt && "no keypath component node");
|
|
auto &comp = KPE->getComponents()[kpElt->getIndex()];
|
|
|
|
if (comp.getKind() == Component::Kind::UnresolvedMember) {
|
|
buildKeyPathMemberComponent(overload, comp.getLoc(), componentLoc,
|
|
components);
|
|
} else if (comp.getKind() == Component::Kind::UnresolvedSubscript) {
|
|
buildKeyPathSubscriptComponent(overload, comp.getLoc(),
|
|
comp.getArgs(), componentLoc,
|
|
components);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
return makeKeyPath(components);
|
|
}
|
|
|
|
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
|
|
buildKeyPathMemberComponent(overload, UDE->getLoc(), componentLoc,
|
|
components);
|
|
} else if (auto *SE = dyn_cast<SubscriptExpr>(anchor)) {
|
|
buildKeyPathSubscriptComponent(overload, SE->getLoc(), SE->getArgs(),
|
|
componentLoc, components);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
return makeKeyPath(components);
|
|
}
|
|
|
|
/// Bridge the given value (which is an error type) to NSError.
|
|
Expr *bridgeErrorToObjectiveC(Expr *value) {
|
|
auto nsErrorType = ctx.getNSErrorType();
|
|
assert(nsErrorType && "Missing NSError?");
|
|
|
|
auto result = new (ctx) BridgeToObjCExpr(value, nsErrorType);
|
|
return cs.cacheType(result);
|
|
}
|
|
|
|
/// Bridge the given value to its corresponding Objective-C object
|
|
/// type.
|
|
///
|
|
/// This routine should only be used for bridging value types.
|
|
///
|
|
/// \param value The value to be bridged.
|
|
Expr *bridgeToObjectiveC(Expr *value, Type objcType) {
|
|
auto result = new (ctx) BridgeToObjCExpr(value, objcType);
|
|
return cs.cacheType(result);
|
|
}
|
|
|
|
/// Bridge the given object from Objective-C to its value type.
|
|
///
|
|
/// This routine should only be used for bridging value types.
|
|
///
|
|
/// \param object The object, whose type should already be of the type
|
|
/// that the value type bridges through.
|
|
///
|
|
/// \param valueType The value type to which we are bridging.
|
|
///
|
|
/// \param conditional Whether the bridging should be conditional. If false,
|
|
/// uses forced bridging.
|
|
///
|
|
/// \returns a value of type \c valueType (optional if \c conditional) that
|
|
/// stores the bridged result or (when \c conditional) an empty optional if
|
|
/// conditional bridging fails.
|
|
Expr *bridgeFromObjectiveC(Expr *object, Type valueType, bool conditional) {
|
|
if (!conditional) {
|
|
auto result = new (ctx) BridgeFromObjCExpr(object, valueType);
|
|
return cs.cacheType(result);
|
|
}
|
|
|
|
// Find the _BridgedToObjectiveC protocol.
|
|
auto bridgedProto =
|
|
ctx.getProtocol(KnownProtocolKind::ObjectiveCBridgeable);
|
|
|
|
// Try to find the conformance of the value type to _BridgedToObjectiveC.
|
|
auto bridgedToObjectiveCConformance
|
|
= checkConformance(valueType, bridgedProto);
|
|
|
|
FuncDecl *fn = nullptr;
|
|
|
|
if (bridgedToObjectiveCConformance) {
|
|
assert(bridgedToObjectiveCConformance.getConditionalRequirements()
|
|
.empty() &&
|
|
"cannot conditionally conform to _BridgedToObjectiveC");
|
|
// The conformance to _BridgedToObjectiveC is statically known.
|
|
// Retrieve the bridging operation to be used if a static conformance
|
|
// to _BridgedToObjectiveC can be proven.
|
|
fn = conditional ? ctx.getConditionallyBridgeFromObjectiveCBridgeable()
|
|
: ctx.getForceBridgeFromObjectiveCBridgeable();
|
|
} else {
|
|
// Retrieve the bridging operation to be used if a static conformance
|
|
// to _BridgedToObjectiveC cannot be proven.
|
|
fn = conditional ? ctx.getConditionallyBridgeFromObjectiveC()
|
|
: ctx.getForceBridgeFromObjectiveC();
|
|
}
|
|
|
|
if (!fn) {
|
|
ctx.Diags.diagnose(object->getLoc(), diag::missing_bridging_function,
|
|
conditional);
|
|
return nullptr;
|
|
}
|
|
|
|
// Form a reference to the function. The bridging operations are generic,
|
|
// so we need to form substitutions and compute the resulting type.
|
|
auto genericSig = fn->getGenericSignature();
|
|
auto subMap = SubstitutionMap::get(
|
|
genericSig, valueType, bridgedToObjectiveCConformance);
|
|
|
|
ConcreteDeclRef fnSpecRef(fn, subMap);
|
|
|
|
auto resultType = OptionalType::get(valueType);
|
|
|
|
auto result = new (ctx)
|
|
ConditionalBridgeFromObjCExpr(object, resultType, fnSpecRef);
|
|
return cs.cacheType(result);
|
|
}
|
|
|
|
/// Bridge the given object from Objective-C to its value type.
|
|
///
|
|
/// This routine should only be used for bridging value types.
|
|
///
|
|
/// \param object The object, whose type should already be of the type
|
|
/// that the value type bridges through.
|
|
///
|
|
/// \param valueType The value type to which we are bridging.
|
|
///
|
|
/// \returns a value of type \c valueType that stores the bridged result.
|
|
Expr *forceBridgeFromObjectiveC(Expr *object, Type valueType) {
|
|
return bridgeFromObjectiveC(object, valueType, false);
|
|
}
|
|
|
|
public:
|
|
/// Simplify the expression type and return the expression.
|
|
///
|
|
/// This routine is used for 'simple' expressions that only need their
|
|
/// types simplified, with no further computation.
|
|
Expr *simplifyExprType(Expr *expr) {
|
|
auto toType = simplifyType(cs.getType(expr));
|
|
cs.setType(expr, toType);
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitErrorExpr(ErrorExpr *expr) {
|
|
// Do nothing with error expressions.
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitCodeCompletionExpr(CodeCompletionExpr *expr) {
|
|
// Do nothing with code completion expressions.
|
|
auto toType = simplifyType(cs.getType(expr));
|
|
cs.setType(expr, toType);
|
|
return expr;
|
|
}
|
|
|
|
Expr *handleIntegerLiteralExpr(LiteralExpr *expr) {
|
|
// If the literal has been assigned a builtin integer type,
|
|
// don't mess with it.
|
|
if (cs.getType(expr)->is<AnyBuiltinIntegerType>())
|
|
return expr;
|
|
|
|
ProtocolDecl *protocol = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(),
|
|
KnownProtocolKind::ExpressibleByIntegerLiteral);
|
|
ProtocolDecl *builtinProtocol = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(),
|
|
KnownProtocolKind::ExpressibleByBuiltinIntegerLiteral);
|
|
|
|
// For type-sugar reasons, prefer the spelling of the default literal
|
|
// type.
|
|
auto type = simplifyType(cs.getType(expr));
|
|
if (auto defaultType = TypeChecker::getDefaultType(protocol, dc)) {
|
|
if (defaultType->isEqual(type))
|
|
type = defaultType;
|
|
}
|
|
if (auto floatProtocol = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(),
|
|
KnownProtocolKind::ExpressibleByFloatLiteral)) {
|
|
if (auto defaultFloatType =
|
|
TypeChecker::getDefaultType(floatProtocol, dc)) {
|
|
if (defaultFloatType->isEqual(type))
|
|
type = defaultFloatType;
|
|
}
|
|
}
|
|
|
|
DeclName initName(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_integerLiteral});
|
|
DeclName builtinInitName(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_builtinIntegerLiteral});
|
|
|
|
auto *result = convertLiteralInPlace(
|
|
expr, type, protocol, ctx.Id_IntegerLiteralType, initName,
|
|
builtinProtocol, builtinInitName, diag::integer_literal_broken_proto,
|
|
diag::builtin_integer_literal_broken_proto);
|
|
if (result) {
|
|
// TODO: It seems that callers expect this to have types assigned...
|
|
result->setType(cs.getType(result));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Expr *visitNilLiteralExpr(NilLiteralExpr *expr) {
|
|
auto type = simplifyType(cs.getType(expr));
|
|
|
|
// By far the most common 'nil' literal is for Optional<T>.none.
|
|
// We don't have to look up the witness in this case since SILGen
|
|
// knows how to lower it directly.
|
|
if (auto objectType = type->getOptionalObjectType()) {
|
|
cs.setType(expr, type);
|
|
return expr;
|
|
}
|
|
|
|
auto *protocol = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(), KnownProtocolKind::ExpressibleByNilLiteral);
|
|
|
|
// For type-sugar reasons, prefer the spelling of the default literal
|
|
// type.
|
|
if (auto defaultType = TypeChecker::getDefaultType(protocol, dc)) {
|
|
if (defaultType->isEqual(type))
|
|
type = defaultType;
|
|
}
|
|
|
|
DeclName initName(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_nilLiteral});
|
|
return convertLiteralInPlace(expr, type, protocol,
|
|
Identifier(), initName,
|
|
nullptr,
|
|
Identifier(),
|
|
diag::nil_literal_broken_proto,
|
|
diag::nil_literal_broken_proto);
|
|
}
|
|
|
|
|
|
Expr *visitIntegerLiteralExpr(IntegerLiteralExpr *expr) {
|
|
return handleIntegerLiteralExpr(expr);
|
|
}
|
|
|
|
Expr *visitFloatLiteralExpr(FloatLiteralExpr *expr) {
|
|
// If the literal has been assigned a builtin float type,
|
|
// don't mess with it.
|
|
if (cs.getType(expr)->is<BuiltinFloatType>())
|
|
return expr;
|
|
|
|
ProtocolDecl *protocol = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(),
|
|
KnownProtocolKind::ExpressibleByFloatLiteral);
|
|
ProtocolDecl *builtinProtocol = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(),
|
|
KnownProtocolKind::ExpressibleByBuiltinFloatLiteral);
|
|
|
|
// For type-sugar reasons, prefer the spelling of the default literal
|
|
// type.
|
|
auto type = simplifyType(cs.getType(expr));
|
|
if (auto defaultType = TypeChecker::getDefaultType(protocol, dc)) {
|
|
if (defaultType->isEqual(type))
|
|
type = defaultType;
|
|
}
|
|
|
|
// Get the _MaxBuiltinFloatType decl, or look for it if it's not cached.
|
|
auto maxFloatTypeDecl = ctx.get_MaxBuiltinFloatTypeDecl();
|
|
// Presence of this declaration has been validated in CSGen.
|
|
assert(maxFloatTypeDecl);
|
|
|
|
auto maxType = maxFloatTypeDecl->getUnderlyingType();
|
|
|
|
DeclName initName(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_floatLiteral});
|
|
DeclName builtinInitName(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_builtinFloatLiteral});
|
|
|
|
expr->setBuiltinType(maxType);
|
|
return convertLiteralInPlace(
|
|
expr, type, protocol, ctx.Id_FloatLiteralType, initName,
|
|
builtinProtocol, builtinInitName, diag::float_literal_broken_proto,
|
|
diag::builtin_float_literal_broken_proto);
|
|
}
|
|
|
|
Expr *visitBooleanLiteralExpr(BooleanLiteralExpr *expr) {
|
|
if (cs.getType(expr) && cs.getType(expr)->is<BuiltinIntegerType>())
|
|
return expr;
|
|
|
|
ProtocolDecl *protocol = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(),
|
|
KnownProtocolKind::ExpressibleByBooleanLiteral);
|
|
ProtocolDecl *builtinProtocol = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(),
|
|
KnownProtocolKind::ExpressibleByBuiltinBooleanLiteral);
|
|
if (!protocol || !builtinProtocol)
|
|
return nullptr;
|
|
|
|
auto type = simplifyType(cs.getType(expr));
|
|
DeclName initName(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_booleanLiteral});
|
|
DeclName builtinInitName(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_builtinBooleanLiteral});
|
|
return convertLiteralInPlace(
|
|
expr, type, protocol, ctx.Id_BooleanLiteralType, initName,
|
|
builtinProtocol, builtinInitName, diag::boolean_literal_broken_proto,
|
|
diag::builtin_boolean_literal_broken_proto);
|
|
}
|
|
|
|
Expr *handleStringLiteralExpr(LiteralExpr *expr) {
|
|
auto stringLiteral = dyn_cast<StringLiteralExpr>(expr);
|
|
auto magicLiteral = dyn_cast<MagicIdentifierLiteralExpr>(expr);
|
|
assert(bool(stringLiteral) != bool(magicLiteral) &&
|
|
"literal must be either a string literal or a magic literal");
|
|
|
|
auto type = simplifyType(cs.getType(expr));
|
|
|
|
bool isStringLiteral = true;
|
|
bool isGraphemeClusterLiteral = false;
|
|
ProtocolDecl *protocol = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(), KnownProtocolKind::ExpressibleByStringLiteral);
|
|
|
|
if (!checkConformance(type, protocol)) {
|
|
// If the type does not conform to ExpressibleByStringLiteral, it should
|
|
// be ExpressibleByExtendedGraphemeClusterLiteral.
|
|
protocol = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(),
|
|
KnownProtocolKind::ExpressibleByExtendedGraphemeClusterLiteral);
|
|
isStringLiteral = false;
|
|
isGraphemeClusterLiteral = true;
|
|
}
|
|
if (!checkConformance(type, protocol)) {
|
|
// ... or it should be ExpressibleByUnicodeScalarLiteral.
|
|
protocol = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(),
|
|
KnownProtocolKind::ExpressibleByUnicodeScalarLiteral);
|
|
isStringLiteral = false;
|
|
isGraphemeClusterLiteral = false;
|
|
}
|
|
|
|
// For type-sugar reasons, prefer the spelling of the default literal
|
|
// type.
|
|
if (auto defaultType = TypeChecker::getDefaultType(protocol, dc)) {
|
|
if (defaultType->isEqual(type))
|
|
type = defaultType;
|
|
}
|
|
|
|
ProtocolDecl *builtinProtocol;
|
|
Identifier literalType;
|
|
DeclName literalFuncName;
|
|
DeclName builtinLiteralFuncName;
|
|
Diag<> brokenProtocolDiag;
|
|
Diag<> brokenBuiltinProtocolDiag;
|
|
|
|
if (isStringLiteral) {
|
|
literalType = ctx.Id_StringLiteralType;
|
|
|
|
literalFuncName = DeclName(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_stringLiteral});
|
|
|
|
builtinProtocol = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(),
|
|
KnownProtocolKind::ExpressibleByBuiltinStringLiteral);
|
|
builtinLiteralFuncName =
|
|
DeclName(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_builtinStringLiteral,
|
|
ctx.getIdentifier("utf8CodeUnitCount"),
|
|
ctx.getIdentifier("isASCII")});
|
|
if (stringLiteral)
|
|
stringLiteral->setEncoding(StringLiteralExpr::UTF8);
|
|
else
|
|
magicLiteral->setStringEncoding(StringLiteralExpr::UTF8);
|
|
|
|
brokenProtocolDiag = diag::string_literal_broken_proto;
|
|
brokenBuiltinProtocolDiag = diag::builtin_string_literal_broken_proto;
|
|
} else if (isGraphemeClusterLiteral) {
|
|
literalType = ctx.Id_ExtendedGraphemeClusterLiteralType;
|
|
literalFuncName = DeclName(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_extendedGraphemeClusterLiteral});
|
|
builtinLiteralFuncName =
|
|
DeclName(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_builtinExtendedGraphemeClusterLiteral,
|
|
ctx.getIdentifier("utf8CodeUnitCount"),
|
|
ctx.getIdentifier("isASCII")});
|
|
|
|
builtinProtocol = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(),
|
|
KnownProtocolKind::
|
|
ExpressibleByBuiltinExtendedGraphemeClusterLiteral);
|
|
brokenProtocolDiag =
|
|
diag::extended_grapheme_cluster_literal_broken_proto;
|
|
brokenBuiltinProtocolDiag =
|
|
diag::builtin_extended_grapheme_cluster_literal_broken_proto;
|
|
} else {
|
|
// Otherwise, we should have just one Unicode scalar.
|
|
literalType = ctx.Id_UnicodeScalarLiteralType;
|
|
|
|
literalFuncName = DeclName(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_unicodeScalarLiteral});
|
|
builtinLiteralFuncName =
|
|
DeclName(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_builtinUnicodeScalarLiteral});
|
|
|
|
builtinProtocol = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(),
|
|
KnownProtocolKind::ExpressibleByBuiltinUnicodeScalarLiteral);
|
|
|
|
brokenProtocolDiag = diag::unicode_scalar_literal_broken_proto;
|
|
brokenBuiltinProtocolDiag =
|
|
diag::builtin_unicode_scalar_literal_broken_proto;
|
|
|
|
stringLiteral->setEncoding(StringLiteralExpr::OneUnicodeScalar);
|
|
}
|
|
|
|
return convertLiteralInPlace(expr,
|
|
type,
|
|
protocol,
|
|
literalType,
|
|
literalFuncName,
|
|
builtinProtocol,
|
|
builtinLiteralFuncName,
|
|
brokenProtocolDiag,
|
|
brokenBuiltinProtocolDiag);
|
|
}
|
|
|
|
Expr *visitStringLiteralExpr(StringLiteralExpr *expr) {
|
|
return handleStringLiteralExpr(expr);
|
|
}
|
|
|
|
Expr *
|
|
visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *expr) {
|
|
// Figure out the string type we're converting to.
|
|
auto openedType = cs.getType(expr);
|
|
auto type = simplifyType(openedType);
|
|
cs.setType(expr, type);
|
|
|
|
auto loc = expr->getStartLoc();
|
|
|
|
auto fetchProtocolInitWitness =
|
|
[&](KnownProtocolKind protocolKind, Type type,
|
|
ArrayRef<Identifier> argLabels) -> ConcreteDeclRef {
|
|
auto proto = TypeChecker::getProtocol(ctx, loc, protocolKind);
|
|
assert(proto && "Missing string interpolation protocol?");
|
|
|
|
auto conformance = checkConformance(type, proto);
|
|
assert(conformance && "string interpolation type conforms to protocol");
|
|
|
|
DeclName constrName(ctx, DeclBaseName::createConstructor(), argLabels);
|
|
|
|
ConcreteDeclRef witness =
|
|
conformance.getWitnessByName(constrName);
|
|
if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
|
|
return nullptr;
|
|
return witness;
|
|
};
|
|
|
|
auto *interpolationProto = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(),
|
|
KnownProtocolKind::ExpressibleByStringInterpolation);
|
|
auto associatedTypeDecl =
|
|
interpolationProto->getAssociatedType(ctx.Id_StringInterpolation);
|
|
if (associatedTypeDecl == nullptr) {
|
|
ctx.Diags.diagnose(expr->getStartLoc(),
|
|
diag::interpolation_broken_proto);
|
|
return nullptr;
|
|
}
|
|
auto interpolationType =
|
|
simplifyType(DependentMemberType::get(openedType, associatedTypeDecl));
|
|
|
|
// Fetch needed witnesses.
|
|
ConcreteDeclRef builderInit = fetchProtocolInitWitness(
|
|
KnownProtocolKind::StringInterpolationProtocol, interpolationType,
|
|
{ctx.Id_literalCapacity, ctx.Id_interpolationCount});
|
|
if (!builderInit) return nullptr;
|
|
expr->setBuilderInit(builderInit);
|
|
|
|
ConcreteDeclRef resultInit = fetchProtocolInitWitness(
|
|
KnownProtocolKind::ExpressibleByStringInterpolation, type,
|
|
{ctx.Id_stringInterpolation});
|
|
if (!resultInit) return nullptr;
|
|
expr->setInitializer(resultInit);
|
|
|
|
// Make the integer literals for the parameters.
|
|
auto buildExprFromUnsigned = [&](unsigned value) {
|
|
LiteralExpr *expr = IntegerLiteralExpr::createFromUnsigned(ctx, value, loc);
|
|
cs.setType(expr, ctx.getIntType());
|
|
return handleIntegerLiteralExpr(expr);
|
|
};
|
|
|
|
expr->setLiteralCapacityExpr(
|
|
buildExprFromUnsigned(expr->getLiteralCapacity()));
|
|
expr->setInterpolationCountExpr(
|
|
buildExprFromUnsigned(expr->getInterpolationCount()));
|
|
|
|
// This OpaqueValueExpr represents the result of builderInit above in
|
|
// silgen.
|
|
OpaqueValueExpr *interpolationExpr =
|
|
new (ctx) OpaqueValueExpr(expr->getSourceRange(), interpolationType);
|
|
cs.setType(interpolationExpr, interpolationType);
|
|
expr->setInterpolationExpr(interpolationExpr);
|
|
|
|
auto appendingExpr = expr->getAppendingExpr();
|
|
appendingExpr->setSubExpr(interpolationExpr);
|
|
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitRegexLiteralExpr(RegexLiteralExpr *expr) {
|
|
simplifyExprType(expr);
|
|
expr->setInitializer(
|
|
ctx.getRegexInitDecl(cs.getType(expr)));
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *expr) {
|
|
#if SWIFT_BUILD_SWIFT_SYNTAX
|
|
if (ctx.LangOpts.hasFeature(Feature::BuiltinMacros)) {
|
|
auto expandedType = solution.simplifyType(solution.getType(expr));
|
|
cs.setType(expr, expandedType);
|
|
|
|
auto locator = cs.getConstraintLocator(expr);
|
|
auto overload = solution.getOverloadChoice(locator);
|
|
|
|
auto macro = cast<MacroDecl>(overload.choice.getDecl());
|
|
ConcreteDeclRef macroRef = resolveConcreteDeclRef(macro, locator);
|
|
auto *expansion = MacroExpansionExpr::create(
|
|
dc, expr->getStartLoc(),
|
|
/*module name=*/DeclNameRef(), /*module name loc=*/DeclNameLoc(),
|
|
DeclNameRef(macro->getName()),
|
|
DeclNameLoc(expr->getLoc()), SourceLoc(), {}, SourceLoc(), nullptr,
|
|
MacroRole::Expression, /*isImplicit=*/true, expandedType);
|
|
expansion->setMacroRef(macroRef);
|
|
(void)evaluateOrDefault(ctx.evaluator,
|
|
ExpandMacroExpansionExprRequest{expansion},
|
|
std::nullopt);
|
|
if (expansion->getRewritten()) {
|
|
cs.cacheExprTypes(expansion);
|
|
return expansion;
|
|
}
|
|
|
|
// Fall through to use old implementation.
|
|
}
|
|
#endif
|
|
|
|
switch (expr->getKind()) {
|
|
#define MAGIC_STRING_IDENTIFIER(NAME, STRING) \
|
|
case MagicIdentifierLiteralExpr::NAME: \
|
|
return handleStringLiteralExpr(expr);
|
|
#define MAGIC_INT_IDENTIFIER(NAME, STRING) \
|
|
case MagicIdentifierLiteralExpr::NAME: \
|
|
return handleIntegerLiteralExpr(expr);
|
|
#define MAGIC_POINTER_IDENTIFIER(NAME, STRING) \
|
|
case MagicIdentifierLiteralExpr::NAME: \
|
|
return expr;
|
|
#include "swift/AST/MagicIdentifierKinds.def"
|
|
}
|
|
|
|
|
|
llvm_unreachable("Unhandled MagicIdentifierLiteralExpr in switch.");
|
|
}
|
|
|
|
Expr *visitObjectLiteralExpr(ObjectLiteralExpr *expr) {
|
|
if (cs.getType(expr) && !cs.getType(expr)->hasTypeVariable())
|
|
return expr;
|
|
|
|
// Figure out the type we're converting to.
|
|
auto openedType = cs.getType(expr);
|
|
auto type = simplifyType(openedType);
|
|
cs.setType(expr, type);
|
|
|
|
Type conformingType = type;
|
|
if (auto baseType = conformingType->getOptionalObjectType()) {
|
|
// The type may be optional due to a failable initializer in the
|
|
// protocol.
|
|
conformingType = baseType;
|
|
}
|
|
|
|
// Find the appropriate object literal protocol.
|
|
auto proto = TypeChecker::getLiteralProtocol(ctx, expr);
|
|
assert(proto && "Missing object literal protocol?");
|
|
auto conformance = checkConformance(conformingType, proto);
|
|
assert(conformance && "object literal type conforms to protocol");
|
|
|
|
auto constrName = TypeChecker::getObjectLiteralConstructorName(ctx, expr);
|
|
|
|
ConcreteDeclRef witness = conformance.getWitnessByName(constrName);
|
|
|
|
auto selectedOverload = solution.getOverloadChoice(
|
|
cs.getConstraintLocator(expr, ConstraintLocator::ConstructorMember));
|
|
|
|
auto fnType = simplifyType(selectedOverload.adjustedOpenedType)
|
|
->castTo<FunctionType>();
|
|
|
|
auto newArgs = coerceCallArguments(
|
|
expr->getArgs(), fnType, witness, /*applyExpr=*/nullptr,
|
|
cs.getConstraintLocator(expr, ConstraintLocator::ApplyArgument),
|
|
/*appliedPropertyWrappers=*/{});
|
|
|
|
expr->setInitializer(witness);
|
|
expr->setArgs(newArgs);
|
|
return expr;
|
|
}
|
|
|
|
/// Add an implicit force unwrap of an expression that references an
|
|
/// implicitly unwrapped optional T!.
|
|
Expr *forceUnwrapIUO(Expr *expr) {
|
|
auto optTy = cs.getType(expr);
|
|
auto objectTy = optTy->getWithoutSpecifierType()->getOptionalObjectType();
|
|
assert(objectTy && "Trying to unwrap non-optional?");
|
|
|
|
// Preserve l-valueness of the result.
|
|
if (optTy->is<LValueType>())
|
|
objectTy = LValueType::get(objectTy);
|
|
|
|
expr = new (ctx) ForceValueExpr(expr, expr->getEndLoc(),
|
|
/*forcedIUO*/ true);
|
|
cs.setType(expr, objectTy);
|
|
expr->setImplicit();
|
|
return expr;
|
|
}
|
|
|
|
/// Retrieve the number of implicit force unwraps required for an implicitly
|
|
/// unwrapped optional reference at a given locator.
|
|
unsigned getIUOForceUnwrapCount(ConstraintLocatorBuilder locator,
|
|
IUOReferenceKind refKind) {
|
|
// Adjust the locator depending on the type of IUO reference.
|
|
auto loc = locator;
|
|
switch (refKind) {
|
|
case IUOReferenceKind::ReturnValue:
|
|
loc = locator.withPathElement(ConstraintLocator::FunctionResult);
|
|
break;
|
|
case IUOReferenceKind::Value:
|
|
break;
|
|
}
|
|
auto *iuoLocator = cs.getConstraintLocator(
|
|
loc, {ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice});
|
|
auto *dynamicLocator = cs.getConstraintLocator(
|
|
loc, {ConstraintLocator::DynamicLookupResult});
|
|
|
|
// First check whether we recorded an implicit unwrap for an IUO.
|
|
unsigned unwrapCount = 0;
|
|
if (solution.DisjunctionChoices.lookup(iuoLocator))
|
|
unwrapCount += 1;
|
|
|
|
// Next check if we recorded an implicit unwrap for dynamic lookup.
|
|
if (solution.DisjunctionChoices.lookup(dynamicLocator))
|
|
unwrapCount += 1;
|
|
|
|
return unwrapCount;
|
|
}
|
|
|
|
Expr *
|
|
forceUnwrapIfExpected(Expr *expr, ConstraintLocatorBuilder locator,
|
|
IUOReferenceKind refKind = IUOReferenceKind::Value) {
|
|
auto unwrapCount = getIUOForceUnwrapCount(locator, refKind);
|
|
for (unsigned i = 0; i < unwrapCount; ++i)
|
|
expr = forceUnwrapIUO(expr);
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitDeclRefExpr(DeclRefExpr *expr) {
|
|
auto locator = cs.getConstraintLocator(expr);
|
|
|
|
// Check whether this is a reference to `__buildSelf`, and if so,
|
|
// replace it with a type expression with fully resolved type.
|
|
if (auto *var = dyn_cast<VarDecl>(expr->getDecl())) {
|
|
if (var->getName() == ctx.Id_builderSelf) {
|
|
assert(expr->isImplicit() && var->isImplicit());
|
|
auto builderTy =
|
|
solution.getResolvedType(var)->getMetatypeInstanceType();
|
|
|
|
return cs.cacheType(
|
|
TypeExpr::createImplicitHack(expr->getLoc(), builderTy, ctx));
|
|
}
|
|
}
|
|
|
|
// Find the overload choice used for this declaration reference.
|
|
auto selected = solution.getOverloadChoice(locator);
|
|
return buildDeclRef(selected, expr->getNameLoc(), locator,
|
|
expr->isImplicit());
|
|
}
|
|
|
|
Expr *visitSuperRefExpr(SuperRefExpr *expr) {
|
|
simplifyExprType(expr);
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitTypeExpr(TypeExpr *expr) {
|
|
auto toType = simplifyType(cs.getType(expr));
|
|
assert(toType->is<MetatypeType>());
|
|
cs.setType(expr, toType);
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitTypeValueExpr(TypeValueExpr *expr) {
|
|
auto toType = simplifyType(cs.getType(expr));
|
|
ASSERT(toType->isEqual(expr->getParamDecl()->getValueType()));
|
|
cs.setType(expr, toType);
|
|
|
|
auto declRefRepr = cast<DeclRefTypeRepr>(expr->getRepr());
|
|
auto resolvedTy =
|
|
TypeResolution::resolveContextualType(declRefRepr, cs.DC,
|
|
TypeResolverContext::InExpression,
|
|
nullptr, nullptr, nullptr);
|
|
|
|
if (!resolvedTy || resolvedTy->hasError())
|
|
return nullptr;
|
|
|
|
expr->setParamType(resolvedTy);
|
|
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitOtherConstructorDeclRefExpr(OtherConstructorDeclRefExpr *expr) {
|
|
cs.setType(expr, expr->getDecl()->getInitializerInterfaceType());
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *expr) {
|
|
return simplifyExprType(expr);
|
|
}
|
|
|
|
Expr *visitOverloadedDeclRefExpr(OverloadedDeclRefExpr *expr) {
|
|
// Determine the declaration selected for this overloaded reference.
|
|
auto locator = cs.getConstraintLocator(expr);
|
|
auto selected = solution.getOverloadChoice(locator);
|
|
|
|
return buildDeclRef(selected, expr->getNameLoc(), locator,
|
|
expr->isImplicit());
|
|
}
|
|
|
|
Expr *visitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *expr) {
|
|
// FIXME: We should have generated an overload set from this, in which
|
|
// case we can emit a typo-correction error here but recover well.
|
|
return nullptr;
|
|
}
|
|
|
|
Expr *visitUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *expr) {
|
|
// Our specializations should have resolved the subexpr to the right type.
|
|
return expr->getSubExpr();
|
|
}
|
|
|
|
Expr *visitMemberRefExpr(MemberRefExpr *expr) {
|
|
auto memberLocator = cs.getConstraintLocator(expr,
|
|
ConstraintLocator::Member);
|
|
auto selected = solution.getOverloadChoice(memberLocator);
|
|
return buildMemberRef(
|
|
expr->getBase(), expr->getDotLoc(), selected, expr->getNameLoc(),
|
|
cs.getConstraintLocator(expr), memberLocator, expr->isImplicit(),
|
|
expr->getAccessSemantics());
|
|
}
|
|
|
|
Expr *visitDynamicMemberRefExpr(DynamicMemberRefExpr *expr) {
|
|
llvm_unreachable("already type-checked?");
|
|
}
|
|
|
|
Expr *visitUnresolvedMemberExpr(UnresolvedMemberExpr *expr) {
|
|
Type resultTy = simplifyType(cs.getType(expr));
|
|
|
|
// Find the selected member and base type.
|
|
auto memberLocator = cs.getConstraintLocator(
|
|
expr, ConstraintLocator::UnresolvedMember);
|
|
auto selected = solution.getOverloadChoice(memberLocator);
|
|
|
|
// Unresolved member lookup always happens in a metatype so dig out the
|
|
// instance type.
|
|
auto baseTy = selected.choice.getBaseType()->getMetatypeInstanceType();
|
|
baseTy = simplifyType(baseTy);
|
|
|
|
// The base expression is simply the metatype of the base type.
|
|
// FIXME: This location info is bogus.
|
|
auto base = TypeExpr::createImplicitHack(expr->getDotLoc(), baseTy, ctx);
|
|
cs.cacheExprTypes(base);
|
|
|
|
// Build the member reference.
|
|
auto *exprLoc = cs.getConstraintLocator(expr);
|
|
auto result = buildMemberRef(
|
|
base, expr->getDotLoc(), selected, expr->getNameLoc(), exprLoc,
|
|
memberLocator, expr->isImplicit(), AccessSemantics::Ordinary);
|
|
if (!result)
|
|
return nullptr;
|
|
|
|
return coerceToType(result, resultTy, cs.getConstraintLocator(expr));
|
|
}
|
|
|
|
private:
|
|
/// A list of "suspicious" optional injections.
|
|
SmallVector<InjectIntoOptionalExpr *, 4> SuspiciousOptionalInjections;
|
|
|
|
/// A list of implicit coercions of noncopyable types.
|
|
SmallVector<Expr *, 4> ConsumingCoercions;
|
|
|
|
/// Create a member reference to the given constructor.
|
|
Expr *applyCtorRefExpr(Expr *expr, Expr *base, SourceLoc dotLoc,
|
|
DeclNameLoc nameLoc, bool implicit,
|
|
ConstraintLocator *ctorLocator,
|
|
SelectedOverload overload) {
|
|
auto locator = cs.getConstraintLocator(expr);
|
|
auto choice = overload.choice;
|
|
assert(choice.getKind() != OverloadChoiceKind::DeclViaDynamic);
|
|
auto *ctor = cast<ConstructorDecl>(choice.getDecl());
|
|
|
|
// If the subexpression is a metatype, build a direct reference to the
|
|
// constructor.
|
|
if (cs.getType(base)->is<AnyMetatypeType>()) {
|
|
return buildMemberRef(base, dotLoc, overload, nameLoc, locator,
|
|
ctorLocator, implicit, AccessSemantics::Ordinary);
|
|
}
|
|
|
|
// The subexpression must be either 'self' or 'super'.
|
|
if (!base->isSuperExpr()) {
|
|
// 'super' references have already been fully checked; handle the
|
|
// 'self' case below.
|
|
auto &de = ctx.Diags;
|
|
bool diagnoseBadInitRef = true;
|
|
auto arg = base->getSemanticsProvidingExpr();
|
|
if (auto dre = dyn_cast<DeclRefExpr>(arg)) {
|
|
if (dre->getDecl()->getName() == ctx.Id_self) {
|
|
// We have a reference to 'self'.
|
|
diagnoseBadInitRef = false;
|
|
// Make sure the reference to 'self' occurs within an initializer.
|
|
if (!dyn_cast_or_null<ConstructorDecl>(
|
|
dc->getInnermostMethodContext())) {
|
|
if (!SuppressDiagnostics)
|
|
de.diagnose(dotLoc, diag::init_delegation_outside_initializer);
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we need to diagnose this as a bad reference to an initializer,
|
|
// do so now.
|
|
if (diagnoseBadInitRef) {
|
|
// Determine whether 'super' would have made sense as a base.
|
|
bool hasSuper = false;
|
|
if (auto func = dc->getInnermostMethodContext()) {
|
|
if (auto classDecl = func->getDeclContext()->getSelfClassDecl()) {
|
|
hasSuper = classDecl->hasSuperclass();
|
|
}
|
|
}
|
|
|
|
if (SuppressDiagnostics)
|
|
return nullptr;
|
|
|
|
de.diagnose(dotLoc, diag::bad_init_ref_base, hasSuper);
|
|
}
|
|
}
|
|
|
|
// Build a partial application of the delegated initializer.
|
|
auto callee = resolveConcreteDeclRef(ctor, ctorLocator);
|
|
Expr *ctorRef = buildOtherConstructorRef(overload.adjustedOpenedFullType, callee,
|
|
base, nameLoc, ctorLocator,
|
|
implicit);
|
|
auto *call =
|
|
DotSyntaxCallExpr::create(ctx, ctorRef, dotLoc,
|
|
Argument::unlabeled(base));
|
|
|
|
return finishApply(call, cs.getType(expr), locator, ctorLocator);
|
|
}
|
|
|
|
/// Give the deprecation warning for referring to a global function
|
|
/// when there's a method from a conditional conformance in a smaller/closer
|
|
/// scope.
|
|
void
|
|
diagnoseDeprecatedConditionalConformanceOuterAccess(UnresolvedDotExpr *UDE,
|
|
ValueDecl *choice) {
|
|
auto getValueDecl = [](DeclContext *context) -> ValueDecl * {
|
|
if (auto generic = context->getSelfNominalTypeDecl()) {
|
|
return generic;
|
|
} else if (context->isModuleScopeContext())
|
|
return context->getParentModule();
|
|
else
|
|
llvm_unreachable("Unsupported base");
|
|
};
|
|
|
|
auto result =
|
|
TypeChecker::lookupUnqualified(dc, UDE->getName(), UDE->getLoc());
|
|
assert(result && "names can't just disappear");
|
|
// These should all come from the same place.
|
|
auto exampleInner = result.front();
|
|
auto innerChoice = exampleInner.getValueDecl();
|
|
auto innerDC = exampleInner.getDeclContext()->getInnermostTypeContext();
|
|
auto innerParentDecl = getValueDecl(innerDC);
|
|
|
|
auto choiceDC = choice->getDeclContext();
|
|
auto choiceParentDecl = getValueDecl(choiceDC);
|
|
|
|
auto &DE = ctx.Diags;
|
|
DE.diagnose(UDE->getLoc(),
|
|
diag::warn_deprecated_conditional_conformance_outer_access,
|
|
UDE->getName(), choice, choiceParentDecl, innerChoice,
|
|
innerParentDecl);
|
|
|
|
auto name = choiceParentDecl->getName().getBaseIdentifier();
|
|
SmallString<32> namePlusDot = name.str();
|
|
namePlusDot.push_back('.');
|
|
|
|
DE.diagnose(UDE->getLoc(),
|
|
diag::fix_deprecated_conditional_conformance_outer_access,
|
|
namePlusDot, choice)
|
|
.fixItInsert(UDE->getStartLoc(), namePlusDot);
|
|
}
|
|
|
|
Expr *applyMemberRefExpr(Expr *expr, Expr *base, SourceLoc dotLoc,
|
|
DeclNameLoc nameLoc, bool implicit) {
|
|
// If we have a constructor member, handle it as a constructor.
|
|
auto ctorLocator = cs.getConstraintLocator(
|
|
expr,
|
|
ConstraintLocator::ConstructorMember);
|
|
if (auto selected = solution.getOverloadChoiceIfAvailable(ctorLocator)) {
|
|
return applyCtorRefExpr(
|
|
expr, base, dotLoc, nameLoc, implicit, ctorLocator, *selected);
|
|
}
|
|
|
|
// Determine the declaration selected for this overloaded reference.
|
|
auto memberLocator = cs.getConstraintLocator(expr,
|
|
ConstraintLocator::Member);
|
|
auto selected = solution.getOverloadChoice(memberLocator);
|
|
|
|
if (!selected.choice.getBaseType()) {
|
|
// This is one of the "outer alternatives", meaning the innermost
|
|
// methods didn't work out.
|
|
//
|
|
// The only way to get here is via an UnresolvedDotExpr with outer
|
|
// alternatives.
|
|
auto UDE = cast<UnresolvedDotExpr>(expr);
|
|
diagnoseDeprecatedConditionalConformanceOuterAccess(
|
|
UDE, selected.choice.getDecl());
|
|
|
|
return buildDeclRef(selected, nameLoc, memberLocator, implicit);
|
|
}
|
|
|
|
switch (selected.choice.getKind()) {
|
|
case OverloadChoiceKind::DeclViaBridge: {
|
|
base = cs.coerceToRValue(base);
|
|
|
|
// Look through an implicitly unwrapped optional.
|
|
auto baseTy = cs.getType(base);
|
|
auto baseMetaTy = baseTy->getAs<MetatypeType>();
|
|
auto baseInstTy = (baseMetaTy ? baseMetaTy->getInstanceType() : baseTy);
|
|
auto classTy = ctx.getBridgedToObjC(dc, baseInstTy);
|
|
|
|
if (baseMetaTy) {
|
|
// FIXME: We're dropping side effects in the base here!
|
|
base = TypeExpr::createImplicitHack(base->getLoc(), classTy, ctx);
|
|
cs.cacheExprTypes(base);
|
|
} else {
|
|
// Bridge the base to its corresponding Objective-C object.
|
|
base = bridgeToObjectiveC(base, classTy);
|
|
}
|
|
|
|
// Fall through to build the member reference.
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
|
|
case OverloadChoiceKind::Decl:
|
|
case OverloadChoiceKind::DeclViaUnwrappedOptional:
|
|
case OverloadChoiceKind::DeclViaDynamic:
|
|
return buildMemberRef(base, dotLoc, selected, nameLoc,
|
|
cs.getConstraintLocator(expr), memberLocator,
|
|
implicit, AccessSemantics::Ordinary);
|
|
|
|
case OverloadChoiceKind::TupleIndex: {
|
|
Type toType = simplifyType(cs.getType(expr));
|
|
|
|
auto baseTy = cs.getType(base);
|
|
// If the base type is not a tuple l-value, access to
|
|
// its elements supposed to be r-value as well.
|
|
//
|
|
// This is modeled in constraint system in a way
|
|
// that when member type is resolved by `resolveOverload`
|
|
// it would take r-value type of the element at
|
|
// specified index, but if it's a type variable it
|
|
// could still be bound to l-value later.
|
|
if (!baseTy->is<LValueType>())
|
|
toType = toType->getRValueType();
|
|
|
|
// If the result type is an rvalue and the base contains lvalues,
|
|
// need a full tuple coercion to properly load & set access kind
|
|
// on all underlying elements before taking a single element.
|
|
if (!toType->hasLValueType() && baseTy->hasLValueType())
|
|
base = coerceToType(base, baseTy->getRValueType(),
|
|
cs.getConstraintLocator(base));
|
|
|
|
return cs.cacheType(new (ctx)
|
|
TupleElementExpr(base, dotLoc,
|
|
selected.choice.getTupleIndex(),
|
|
nameLoc.getBaseNameLoc(), toType));
|
|
}
|
|
|
|
case OverloadChoiceKind::MaterializePack: {
|
|
auto baseTy = solution.getResolvedType(base);
|
|
|
|
// Load the base tuple if necessary, materialization
|
|
// operates on r-value types only.
|
|
if (baseTy->is<LValueType>())
|
|
base = coerceToType(base, baseTy->getRValueType(),
|
|
cs.getConstraintLocator(base));
|
|
|
|
auto packType = solution.getResolvedType(expr);
|
|
return cs.cacheType(
|
|
MaterializePackExpr::create(ctx,
|
|
base, expr->getEndLoc(),
|
|
packType));
|
|
}
|
|
|
|
case OverloadChoiceKind::ExtractFunctionIsolation: {
|
|
auto isolationType = solution.getResolvedType(expr);
|
|
auto *extractExpr = new (ctx)
|
|
ExtractFunctionIsolationExpr(base,
|
|
expr->getEndLoc(),
|
|
isolationType);
|
|
return cs.cacheType(extractExpr);
|
|
}
|
|
|
|
case OverloadChoiceKind::KeyPathApplication:
|
|
llvm_unreachable("should only happen in a subscript");
|
|
|
|
case OverloadChoiceKind::DynamicMemberLookup:
|
|
case OverloadChoiceKind::KeyPathDynamicMemberLookup: {
|
|
return buildDynamicMemberLookupRef(
|
|
expr, base, dotLoc, nameLoc.getStartLoc(), selected, memberLocator);
|
|
}
|
|
}
|
|
|
|
llvm_unreachable("Unhandled OverloadChoiceKind in switch.");
|
|
}
|
|
|
|
/// Form a type checked expression for the argument of a
|
|
/// @dynamicMemberLookup subscript index parameter.
|
|
Expr *buildDynamicMemberLookupArgExpr(StringRef name, SourceLoc loc,
|
|
Type literalTy) {
|
|
// Build and type check the string literal index value to the specific
|
|
// string type expected by the subscript.
|
|
auto *nameExpr = new (ctx) StringLiteralExpr(name, loc, /*implicit*/true);
|
|
cs.setType(nameExpr, literalTy);
|
|
return handleStringLiteralExpr(nameExpr);
|
|
}
|
|
|
|
Expr *buildDynamicMemberLookupRef(Expr *expr, Expr *base, SourceLoc dotLoc,
|
|
SourceLoc nameLoc,
|
|
const SelectedOverload &overload,
|
|
ConstraintLocator *memberLocator) {
|
|
// Application of a DynamicMemberLookup result turns
|
|
// a member access of `x.foo` into x[dynamicMember: "foo"], or
|
|
// x[dynamicMember: KeyPath<T, U>]
|
|
|
|
// Figure out the expected type of the lookup parameter. We know the
|
|
// openedFullType will be "xType -> indexType -> resultType". Dig out
|
|
// its index type.
|
|
auto paramTy = getTypeOfDynamicMemberIndex(overload);
|
|
|
|
Expr *argExpr = nullptr;
|
|
if (overload.choice.getKind() ==
|
|
OverloadChoiceKind::DynamicMemberLookup) {
|
|
// Build and type check the string literal index value to the specific
|
|
// string type expected by the subscript.
|
|
auto fieldName = overload.choice.getName().getBaseIdentifier().str();
|
|
argExpr = buildDynamicMemberLookupArgExpr(fieldName, nameLoc, paramTy);
|
|
} else {
|
|
argExpr =
|
|
buildKeyPathDynamicMemberArgExpr(paramTy, dotLoc, memberLocator);
|
|
}
|
|
|
|
if (!argExpr)
|
|
return nullptr;
|
|
|
|
solution.recordSingleArgMatchingChoice(cs.getConstraintLocator(expr));
|
|
|
|
// Build an argument list.
|
|
auto *argList =
|
|
ArgumentList::forImplicitSingle(ctx, ctx.Id_dynamicMember, argExpr);
|
|
// Build and return a subscript that uses this string as the index.
|
|
return buildSubscript(base, argList, cs.getConstraintLocator(expr),
|
|
memberLocator, /*isImplicit*/ true,
|
|
AccessSemantics::Ordinary, overload);
|
|
}
|
|
|
|
Type getTypeOfDynamicMemberIndex(const SelectedOverload &overload) {
|
|
assert(overload.choice.isAnyDynamicMemberLookup());
|
|
|
|
auto declTy = solution.simplifyType(overload.adjustedOpenedFullType);
|
|
auto subscriptTy = declTy->castTo<FunctionType>()->getResult();
|
|
auto refFnType = subscriptTy->castTo<FunctionType>();
|
|
assert(refFnType->getParams().size() == 1 &&
|
|
"subscript always has one arg");
|
|
return refFnType->getParams()[0].getPlainType();
|
|
}
|
|
|
|
public:
|
|
Expr *visitUnresolvedDotExpr(UnresolvedDotExpr *expr) {
|
|
return applyMemberRefExpr(expr, expr->getBase(), expr->getDotLoc(),
|
|
expr->getNameLoc(), expr->isImplicit());
|
|
}
|
|
|
|
Expr *visitSequenceExpr(SequenceExpr *expr) {
|
|
llvm_unreachable("Expression wasn't parsed?");
|
|
}
|
|
|
|
Expr *visitArrowExpr(ArrowExpr *expr) {
|
|
llvm_unreachable("Arrow expr wasn't converted to type?");
|
|
}
|
|
|
|
Expr *visitIdentityExpr(IdentityExpr *expr) {
|
|
cs.setType(expr, cs.getType(expr->getSubExpr()));
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitCopyExpr(CopyExpr *expr) {
|
|
auto toType = simplifyType(cs.getType(expr));
|
|
cs.setType(expr, toType);
|
|
|
|
auto *subExpr = expr->getSubExpr();
|
|
auto type = simplifyType(cs.getType(subExpr));
|
|
|
|
// Let's load the value associated with this try.
|
|
if (type->hasLValueType()) {
|
|
subExpr = coerceToType(subExpr, type->getRValueType(),
|
|
cs.getConstraintLocator(subExpr));
|
|
|
|
if (!subExpr)
|
|
return nullptr;
|
|
}
|
|
|
|
expr->setSubExpr(subExpr);
|
|
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitConsumeExpr(ConsumeExpr *expr) {
|
|
auto toType = simplifyType(cs.getType(expr));
|
|
cs.setType(expr, toType);
|
|
|
|
auto *subExpr = expr->getSubExpr();
|
|
auto type = simplifyType(cs.getType(subExpr));
|
|
|
|
// Let's load the value associated with this consume.
|
|
if (type->hasLValueType()) {
|
|
subExpr = coerceToType(subExpr, type->getRValueType(),
|
|
cs.getConstraintLocator(subExpr));
|
|
|
|
if (!subExpr)
|
|
return nullptr;
|
|
}
|
|
|
|
expr->setSubExpr(subExpr);
|
|
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitAnyTryExpr(AnyTryExpr *expr) {
|
|
auto *subExpr = expr->getSubExpr();
|
|
auto type = simplifyType(cs.getType(subExpr));
|
|
|
|
// Let's load the value associated with this try.
|
|
if (type->hasLValueType()) {
|
|
subExpr = coerceToType(subExpr, type->getRValueType(),
|
|
cs.getConstraintLocator(subExpr));
|
|
|
|
if (!subExpr)
|
|
return nullptr;
|
|
}
|
|
|
|
cs.setType(expr, cs.getType(subExpr));
|
|
expr->setSubExpr(subExpr);
|
|
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitOptionalTryExpr(OptionalTryExpr *expr) {
|
|
// Prior to Swift 5, 'try?' simply wraps the type of its sub-expression
|
|
// in an Optional, regardless of the sub-expression type.
|
|
//
|
|
// In Swift 5+, the type of a 'try?' expression of static type T is:
|
|
// - Equal to T if T is optional
|
|
// - Equal to T? if T is not optional
|
|
//
|
|
// The result is that in Swift 5, 'try?' avoids producing nested optionals.
|
|
|
|
if (!ctx.LangOpts.isSwiftVersionAtLeast(5)) {
|
|
// Nothing to do for Swift 4 and earlier!
|
|
return simplifyExprType(expr);
|
|
}
|
|
|
|
Type exprType = simplifyType(cs.getType(expr));
|
|
|
|
auto subExpr = coerceToType(expr->getSubExpr(), exprType,
|
|
cs.getConstraintLocator(expr));
|
|
if (!subExpr) return nullptr;
|
|
expr->setSubExpr(subExpr);
|
|
|
|
cs.setType(expr, exprType);
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitParenExpr(ParenExpr *expr) {
|
|
Expr *result = expr;
|
|
auto type = simplifyType(cs.getType(expr));
|
|
|
|
// A ParenExpr can end up with a tuple type if it contains
|
|
// a pack expansion. Rewrite it to a TupleExpr.
|
|
if (dyn_cast<PackExpansionExpr>(expr->getSubExpr())) {
|
|
result = TupleExpr::create(ctx, expr->getLParenLoc(),
|
|
{expr->getSubExpr()},
|
|
/*elementNames=*/{},
|
|
/*elementNameLocs=*/{},
|
|
expr->getRParenLoc(),
|
|
expr->isImplicit());
|
|
}
|
|
|
|
cs.setType(result, type);
|
|
return result;
|
|
}
|
|
|
|
Expr *visitUnresolvedMemberChainResultExpr(
|
|
UnresolvedMemberChainResultExpr *expr) {
|
|
// Since this expression only exists to give the result type of an
|
|
// unresolved member chain visibility in the AST, remove it from the AST
|
|
// now that we have a solution and coerce the subexpr to the resulting
|
|
// type.
|
|
auto *subExpr = expr->getSubExpr();
|
|
auto type = simplifyType(cs.getType(expr));
|
|
subExpr = coerceToType(
|
|
subExpr, type,
|
|
cs.getConstraintLocator(
|
|
expr, ConstraintLocator::UnresolvedMemberChainResult));
|
|
cs.setType(subExpr, type);
|
|
return subExpr;
|
|
}
|
|
|
|
Expr *visitTupleExpr(TupleExpr *expr) {
|
|
return simplifyExprType(expr);
|
|
}
|
|
|
|
Expr *visitSubscriptExpr(SubscriptExpr *expr) {
|
|
auto *memberLocator =
|
|
cs.getConstraintLocator(expr, ConstraintLocator::SubscriptMember);
|
|
auto overload = solution.getOverloadChoice(memberLocator);
|
|
if (overload.choice.isKeyPathDynamicMemberLookup()) {
|
|
return buildDynamicMemberLookupRef(
|
|
expr, expr->getBase(), expr->getArgs()->getStartLoc(), SourceLoc(),
|
|
overload, memberLocator);
|
|
}
|
|
|
|
return buildSubscript(expr->getBase(), expr->getArgs(),
|
|
cs.getConstraintLocator(expr), memberLocator,
|
|
expr->isImplicit(), expr->getAccessSemantics(),
|
|
overload);
|
|
}
|
|
|
|
/// "Finish" an array expression by filling in the semantic expression.
|
|
ArrayExpr *finishArrayExpr(ArrayExpr *expr) {
|
|
Type arrayTy = cs.getType(expr);
|
|
Type elementType;
|
|
|
|
if (arrayTy->isInlineArray()) {
|
|
// <let count: Int, Element>
|
|
elementType = arrayTy->castTo<BoundGenericStructType>()->getGenericArgs()[1];
|
|
} else {
|
|
ProtocolDecl *arrayProto = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(), KnownProtocolKind::ExpressibleByArrayLiteral);
|
|
assert(arrayProto && "type-checked array literal w/o protocol?!");
|
|
|
|
auto conformance = checkConformance(arrayTy, arrayProto);
|
|
assert(conformance && "Type does not conform to protocol?");
|
|
|
|
DeclName name(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_arrayLiteral});
|
|
ConcreteDeclRef witness =
|
|
conformance.getWitnessByName(name);
|
|
if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
|
|
return nullptr;
|
|
expr->setInitializer(witness);
|
|
|
|
elementType = expr->getElementType();
|
|
}
|
|
|
|
for (unsigned i = 0, n = expr->getNumElements(); i != n; ++i) {
|
|
expr->setElement(
|
|
i, coerceToType(expr->getElement(i), elementType,
|
|
cs.getConstraintLocator(
|
|
expr, {LocatorPathElt::TupleElement(i)})));
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitArrayExpr(ArrayExpr *expr) {
|
|
Type openedType = cs.getType(expr);
|
|
Type arrayTy = simplifyType(openedType);
|
|
cs.setType(expr, arrayTy);
|
|
if (!finishArrayExpr(expr)) return nullptr;
|
|
|
|
// If the array element type was defaulted, note that in the expression.
|
|
if (solution.DefaultedConstraints.count(cs.getConstraintLocator(expr)))
|
|
expr->setIsTypeDefaulted();
|
|
|
|
return expr;
|
|
}
|
|
|
|
/// "Finish" a dictionary expression by filling in the semantic expression.
|
|
DictionaryExpr *finishDictionaryExpr(DictionaryExpr *expr) {
|
|
Type dictionaryTy = cs.getType(expr);
|
|
|
|
ProtocolDecl *dictionaryProto = TypeChecker::getProtocol(
|
|
ctx, expr->getLoc(),
|
|
KnownProtocolKind::ExpressibleByDictionaryLiteral);
|
|
|
|
auto conformance = checkConformance(dictionaryTy, dictionaryProto);
|
|
if (conformance.isInvalid())
|
|
return nullptr;
|
|
|
|
DeclName name(ctx, DeclBaseName::createConstructor(),
|
|
{ctx.Id_dictionaryLiteral});
|
|
ConcreteDeclRef witness =
|
|
conformance.getWitnessByName(name);
|
|
if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
|
|
return nullptr;
|
|
expr->setInitializer(witness);
|
|
|
|
auto elementType = expr->getElementType();
|
|
|
|
for (unsigned i = 0, n = expr->getNumElements(); i != n; ++i) {
|
|
expr->setElement(
|
|
i, coerceToType(expr->getElement(i), elementType,
|
|
cs.getConstraintLocator(
|
|
expr, {LocatorPathElt::TupleElement(i)})));
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitDictionaryExpr(DictionaryExpr *expr) {
|
|
Type openedType = cs.getType(expr);
|
|
Type dictionaryTy = simplifyType(openedType);
|
|
cs.setType(expr, dictionaryTy);
|
|
if (!finishDictionaryExpr(expr)) return nullptr;
|
|
|
|
// If the dictionary key or value type was defaulted, note that in the
|
|
// expression.
|
|
if (solution.DefaultedConstraints.count(cs.getConstraintLocator(expr)))
|
|
expr->setIsTypeDefaulted();
|
|
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitDynamicSubscriptExpr(DynamicSubscriptExpr *expr) {
|
|
auto *memberLocator =
|
|
cs.getConstraintLocator(expr, ConstraintLocator::SubscriptMember);
|
|
return buildSubscript(expr->getBase(), expr->getArgs(),
|
|
cs.getConstraintLocator(expr), memberLocator,
|
|
expr->isImplicit(), AccessSemantics::Ordinary,
|
|
solution.getOverloadChoice(memberLocator));
|
|
}
|
|
|
|
Expr *visitTupleElementExpr(TupleElementExpr *expr) {
|
|
simplifyExprType(expr);
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitCaptureListExpr(CaptureListExpr *expr) {
|
|
// The type of the capture list is the type of the closure contained
|
|
// inside it.
|
|
cs.setType(expr, cs.getType(expr->getClosureBody()));
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitClosureExpr(ClosureExpr *expr) {
|
|
llvm_unreachable("Handled by the walker directly");
|
|
}
|
|
|
|
Expr *visitAutoClosureExpr(AutoClosureExpr *expr) {
|
|
llvm_unreachable("Already type-checked");
|
|
}
|
|
|
|
Expr *visitInOutExpr(InOutExpr *expr) {
|
|
auto objectTy = cs.getType(expr->getSubExpr())->getRValueType();
|
|
|
|
// The type is simply inout of whatever the lvalue's object type was.
|
|
cs.setType(expr, InOutType::get(objectTy));
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitVarargExpansionExpr(VarargExpansionExpr *expr) {
|
|
simplifyExprType(expr);
|
|
|
|
auto arrayTy = cs.getType(expr);
|
|
expr->setSubExpr(coerceToType(expr->getSubExpr(), arrayTy,
|
|
cs.getConstraintLocator(expr)));
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitPackExpansionExpr(PackExpansionExpr *expr) {
|
|
// Set the opened pack element environment for this pack expansion.
|
|
// Assert that we have an opened element environment, otherwise we'll get
|
|
// an ASTVerifier crash when pack archetypes or element archetypes appear
|
|
// inside the pack expansion expression.
|
|
auto *environment = solution.getPackExpansionEnvironment(expr);
|
|
assert(environment);
|
|
expr->setGenericEnvironment(environment);
|
|
|
|
return simplifyExprType(expr);
|
|
}
|
|
|
|
Expr *visitPackElementExpr(PackElementExpr *expr) {
|
|
if (auto *packRefExpr = expr->getPackRefExpr()) {
|
|
packRefExpr = cs.coerceToRValue(packRefExpr);
|
|
auto packRefType = cs.getType(packRefExpr);
|
|
if (auto patternType =
|
|
getPatternTypeOfSingleUnlabeledPackExpansionTuple(
|
|
packRefType)) {
|
|
auto *materializedPackExpr = MaterializePackExpr::create(
|
|
ctx, packRefExpr, packRefExpr->getLoc(),
|
|
patternType, /*implicit*/ true);
|
|
cs.cacheType(materializedPackExpr);
|
|
expr->setPackRefExpr(materializedPackExpr);
|
|
}
|
|
}
|
|
return simplifyExprType(expr);
|
|
}
|
|
|
|
Expr *visitMaterializePackExpr(MaterializePackExpr *expr) {
|
|
llvm_unreachable("MaterializePackExpr already type-checked");
|
|
}
|
|
|
|
Expr *visitDynamicTypeExpr(DynamicTypeExpr *expr) {
|
|
Expr *base = expr->getBase();
|
|
base = cs.coerceToRValue(base);
|
|
expr->setBase(base);
|
|
|
|
return simplifyExprType(expr);
|
|
}
|
|
|
|
Expr *visitOpaqueValueExpr(OpaqueValueExpr *expr) {
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitPropertyWrapperValuePlaceholderExpr(
|
|
PropertyWrapperValuePlaceholderExpr *expr) {
|
|
// If there is no opaque value placeholder, the enclosing init(wrappedValue:)
|
|
// expression cannot be reused, so we only need the original wrapped value
|
|
// argument in the AST.
|
|
if (!expr->getOpaqueValuePlaceholder())
|
|
return expr->getOriginalWrappedValue();
|
|
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitAppliedPropertyWrapperExpr(AppliedPropertyWrapperExpr *expr) {
|
|
llvm_unreachable("Already type-checked");
|
|
}
|
|
|
|
Expr *visitDefaultArgumentExpr(DefaultArgumentExpr *expr) {
|
|
llvm_unreachable("Already type-checked");
|
|
}
|
|
|
|
Expr *visitApplyExpr(ApplyExpr *expr) {
|
|
auto *calleeLoc = CalleeLocators[expr];
|
|
assert(calleeLoc);
|
|
return finishApply(expr, cs.getType(expr), cs.getConstraintLocator(expr),
|
|
calleeLoc);
|
|
}
|
|
|
|
Expr *visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *expr) {
|
|
Expr *subExpr = expr->getSubExpr();
|
|
auto *inCtor = cast<ConstructorDecl>(dc->getInnermostMethodContext());
|
|
|
|
// A non-failable initializer cannot delegate nor chain to
|
|
// * a throwing initializer via 'try?'
|
|
// * a failable initializer, unless the failability kind is IUO or the
|
|
// result is explicitly forced
|
|
if (!inCtor->isFailable() && cs.getType(subExpr)->isOptional()) {
|
|
bool isChaining;
|
|
auto *otherCtorRef = expr->getCalledConstructor(isChaining);
|
|
auto *otherCtor = otherCtorRef->getDecl();
|
|
assert(otherCtor);
|
|
|
|
Expr *newSubExpr = subExpr;
|
|
auto &de = ctx.Diags;
|
|
|
|
// Look through 'try', 'try!', and identity expressions.
|
|
subExpr = subExpr->getValueProvidingExpr();
|
|
|
|
// Diagnose if we find a 'try?'.
|
|
// FIXME: We could put up with occurrences of 'try?' if they do not apply
|
|
// directly to the called ctor, e.g. 'try? try self.init()', or if the
|
|
// called ctor isn't throwing.
|
|
if (auto *OTE = dyn_cast<OptionalTryExpr>(subExpr)) {
|
|
if (SuppressDiagnostics)
|
|
return nullptr;
|
|
|
|
// Give the user the option of using 'try!' or making the enclosing
|
|
// initializer failable.
|
|
de.diagnose(OTE->getTryLoc(),
|
|
diag::delegate_chain_nonoptional_to_optional_try,
|
|
isChaining);
|
|
de.diagnose(OTE->getTryLoc(), diag::init_delegate_force_try)
|
|
.fixItReplace({OTE->getTryLoc(), OTE->getQuestionLoc()}, "try!");
|
|
de.diagnose(inCtor->getLoc(), diag::init_propagate_failure)
|
|
.fixItInsertAfter(inCtor->getLoc(), "?");
|
|
|
|
subExpr = OTE->getSubExpr();
|
|
}
|
|
|
|
while (true) {
|
|
subExpr = subExpr->getSemanticsProvidingExpr();
|
|
|
|
// Look through optional injections.
|
|
if (auto *IIOE = dyn_cast<InjectIntoOptionalExpr>(subExpr)) {
|
|
subExpr = IIOE->getSubExpr();
|
|
continue;
|
|
}
|
|
|
|
// Look through all try expressions.
|
|
if (auto *ATE = dyn_cast<AnyTryExpr>(subExpr)) {
|
|
subExpr = ATE->getSubExpr();
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// If we're still hitting an optional and the called ctor is failable,
|
|
// diagnose only if the failability kind is not IUO. Note that the
|
|
// expression type alone is not always indicative of the ctor's
|
|
// failability, because it could be declared on 'Optional' itself.
|
|
if (cs.getType(subExpr)->isOptional() && otherCtor->isFailable()) {
|
|
if (!otherCtor->isImplicitlyUnwrappedOptional()) {
|
|
if (SuppressDiagnostics)
|
|
return nullptr;
|
|
|
|
// Give the user the option of adding '!' or making the enclosing
|
|
// initializer failable.
|
|
de.diagnose(otherCtorRef->getLoc(),
|
|
diag::delegate_chain_nonoptional_to_optional,
|
|
isChaining, otherCtor);
|
|
de.diagnose(otherCtorRef->getLoc(), diag::init_force_unwrap)
|
|
.fixItInsertAfter(expr->getEndLoc(), "!");
|
|
de.diagnose(inCtor->getLoc(), diag::init_propagate_failure)
|
|
.fixItInsertAfter(inCtor->getLoc(), "?");
|
|
}
|
|
|
|
// Recover by injecting the force operation (the first option).
|
|
newSubExpr = forceUnwrapIUO(newSubExpr);
|
|
}
|
|
|
|
expr->setSubExpr(newSubExpr);
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitTernaryExpr(TernaryExpr *expr) {
|
|
auto resultTy = simplifyType(cs.getType(expr));
|
|
cs.setType(expr, resultTy);
|
|
|
|
auto cond = cs.coerceToRValue(expr->getCondExpr());
|
|
expr->setCondExpr(cond);
|
|
|
|
// Coerce the then/else branches to the common type.
|
|
expr->setThenExpr(coerceToType(
|
|
expr->getThenExpr(), resultTy,
|
|
cs.getConstraintLocator(expr, LocatorPathElt::TernaryBranch(true))));
|
|
expr->setElseExpr(coerceToType(
|
|
expr->getElseExpr(), resultTy,
|
|
cs.getConstraintLocator(expr, LocatorPathElt::TernaryBranch(false))));
|
|
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitImplicitConversionExpr(ImplicitConversionExpr *expr) {
|
|
llvm_unreachable("Already type-checked");
|
|
}
|
|
|
|
Expr *visitIsExpr(IsExpr *expr) {
|
|
// Turn the subexpression into an rvalue.
|
|
auto sub = cs.coerceToRValue(expr->getSubExpr());
|
|
expr->setSubExpr(sub);
|
|
|
|
// Simplify and update the type we checked against.
|
|
auto *const castTypeRepr = expr->getCastTypeRepr();
|
|
|
|
const auto fromType = cs.getType(sub);
|
|
const auto toType = simplifyType(cs.getType(castTypeRepr));
|
|
expr->setCastType(toType);
|
|
cs.setType(castTypeRepr, toType);
|
|
|
|
auto castKind = TypeChecker::typeCheckCheckedCast(
|
|
fromType, toType, CheckedCastContextKind::IsExpr, dc);
|
|
|
|
switch (castKind) {
|
|
case CheckedCastKind::Unresolved:
|
|
expr->setCastKind(CheckedCastKind::ValueCast);
|
|
break;
|
|
|
|
case CheckedCastKind::Coercion:
|
|
case CheckedCastKind::BridgingCoercion:
|
|
case CheckedCastKind::ValueCast:
|
|
case CheckedCastKind::ArrayDowncast:
|
|
case CheckedCastKind::DictionaryDowncast:
|
|
case CheckedCastKind::SetDowncast:
|
|
// Valid checks.
|
|
expr->setCastKind(castKind);
|
|
break;
|
|
}
|
|
|
|
// SIL-generation magically turns this into a Bool; make sure it can.
|
|
if (!ctx.getBoolBuiltinInitDecl()) {
|
|
ctx.Diags.diagnose(expr->getLoc(), diag::broken_stdlib_type, "Bool");
|
|
// Continue anyway.
|
|
}
|
|
|
|
// Dig through the optionals in the from/to types.
|
|
SmallVector<Type, 2> fromOptionals;
|
|
fromType->lookThroughAllOptionalTypes(fromOptionals);
|
|
SmallVector<Type, 2> toOptionals;
|
|
toType->lookThroughAllOptionalTypes(toOptionals);
|
|
|
|
// If we have an imbalance of optionals or a collection
|
|
// downcast, handle this as a checked cast followed by a
|
|
// a 'hasValue' check.
|
|
if (fromOptionals.size() != toOptionals.size() ||
|
|
castKind == CheckedCastKind::ArrayDowncast ||
|
|
castKind == CheckedCastKind::DictionaryDowncast ||
|
|
castKind == CheckedCastKind::SetDowncast) {
|
|
auto *const cast =
|
|
ConditionalCheckedCastExpr::createImplicit(ctx, sub, toType);
|
|
cast->setType(OptionalType::get(toType));
|
|
cast->setCastType(toType);
|
|
cs.setType(cast, cast->getType());
|
|
|
|
// Type-check this conditional case.
|
|
Expr *result = handleConditionalCheckedCastExpr(cast, castTypeRepr);
|
|
if (!result)
|
|
return nullptr;
|
|
|
|
// Extract a Bool from the resulting expression.
|
|
TypeChecker::requireOptionalIntrinsics(ctx, expr->getLoc());
|
|
|
|
// Match the optional value against its `Some` case.
|
|
auto *someDecl = ctx.getOptionalSomeDecl();
|
|
auto isSomeExpr =
|
|
new (ctx) EnumIsCaseExpr(result, castTypeRepr, someDecl);
|
|
auto boolDecl = ctx.getBoolDecl();
|
|
|
|
if (!boolDecl) {
|
|
ctx.Diags.diagnose(SourceLoc(), diag::broken_stdlib_type, "Bool");
|
|
}
|
|
|
|
cs.setType(isSomeExpr, boolDecl ? ctx.getBoolType() : Type());
|
|
return isSomeExpr;
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
/// The kind of cast we're working with for handling optional bindings.
|
|
enum class OptionalBindingsCastKind {
|
|
/// An explicit bridging conversion, spelled "as".
|
|
Bridged,
|
|
/// A forced cast, spelled "as!".
|
|
Forced,
|
|
/// A conditional cast, spelled "as?".
|
|
Conditional,
|
|
};
|
|
|
|
/// Handle optional operands and results in an explicit cast.
|
|
Expr *handleOptionalBindingsForCast(ExplicitCastExpr *cast,
|
|
Type finalResultType,
|
|
OptionalBindingsCastKind castKind) {
|
|
return handleOptionalBindings(cast->getSubExpr(), finalResultType,
|
|
castKind,
|
|
[&](Expr *sub, Type resultType) -> Expr* {
|
|
|
|
// Complain about conditional casts to CF class types; they can't
|
|
// actually be conditionally checked.
|
|
if (castKind == OptionalBindingsCastKind::Conditional) {
|
|
Type destValueType = resultType->getOptionalObjectType();
|
|
auto destObjectType = destValueType;
|
|
if (auto metaTy = destObjectType->getAs<MetatypeType>())
|
|
destObjectType = metaTy->getInstanceType();
|
|
if (auto destClass = destObjectType->getClassOrBoundGenericClass()) {
|
|
if (destClass->getForeignClassKind() ==
|
|
ClassDecl::ForeignKind::CFType) {
|
|
if (SuppressDiagnostics)
|
|
return nullptr;
|
|
|
|
auto &de = ctx.Diags;
|
|
de.diagnose(cast->getLoc(), diag::conditional_downcast_foreign,
|
|
destValueType);
|
|
ConcreteDeclRef refDecl = sub->getReferencedDecl();
|
|
if (refDecl) {
|
|
de.diagnose(cast->getLoc(),
|
|
diag::note_explicitly_compare_cftypeid,
|
|
refDecl.getDecl()->getBaseName(), destValueType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the expression as the sub-expression of the cast, then
|
|
// use the cast as the inner operation.
|
|
cast->setSubExpr(sub);
|
|
cs.setType(cast, resultType);
|
|
return cast;
|
|
});
|
|
}
|
|
|
|
/// A helper function to build an operation. The inner result type
|
|
/// is the expected type of the operation; it will be a non-optional
|
|
/// type unless the castKind is Conditional.
|
|
using OperationBuilderRef =
|
|
llvm::function_ref<Expr*(Expr *subExpr, Type innerResultType)>;
|
|
|
|
/// Handle optional operands and results in an explicit cast.
|
|
Expr *handleOptionalBindings(Expr *subExpr, Type finalResultType,
|
|
OptionalBindingsCastKind castKind,
|
|
OperationBuilderRef buildInnerOperation) {
|
|
|
|
unsigned destExtraOptionals;
|
|
bool forceExtraSourceOptionals;
|
|
switch (castKind) {
|
|
case OptionalBindingsCastKind::Bridged:
|
|
destExtraOptionals = 0;
|
|
forceExtraSourceOptionals = true;
|
|
break;
|
|
|
|
case OptionalBindingsCastKind::Forced:
|
|
destExtraOptionals = 0;
|
|
forceExtraSourceOptionals = true;
|
|
break;
|
|
|
|
case OptionalBindingsCastKind::Conditional:
|
|
destExtraOptionals = 1;
|
|
forceExtraSourceOptionals = false;
|
|
break;
|
|
}
|
|
|
|
// FIXME: some of this work needs to be delayed until runtime to
|
|
// properly account for archetypes dynamically being optional
|
|
// types. For example, if we're casting T to NSView?, that
|
|
// should succeed if T=NSObject? and its value is actually nil.
|
|
Type srcType = cs.getType(subExpr);
|
|
|
|
SmallVector<Type, 4> srcOptionals;
|
|
srcType = srcType->lookThroughAllOptionalTypes(srcOptionals);
|
|
|
|
SmallVector<Type, 4> destOptionals;
|
|
auto destValueType
|
|
= finalResultType->lookThroughAllOptionalTypes(destOptionals);
|
|
|
|
auto isBridgeToAnyObject =
|
|
castKind == OptionalBindingsCastKind::Bridged &&
|
|
destValueType->isAnyObject();
|
|
|
|
// If the destination value type is 'AnyObject' when performing a
|
|
// bridging operation, or if the destination value type could dynamically
|
|
// be an optional type, leave any extra optionals on the source in place.
|
|
// Only apply the latter condition in Swift 5 mode to best preserve
|
|
// compatibility with Swift 4.1's casting behaviour.
|
|
if (isBridgeToAnyObject || (ctx.isSwiftVersionAtLeast(5) &&
|
|
destValueType->canDynamicallyBeOptionalType(
|
|
/*includeExistential*/ false))) {
|
|
auto destOptionalsCount = destOptionals.size() - destExtraOptionals;
|
|
if (srcOptionals.size() > destOptionalsCount) {
|
|
srcType = srcOptionals[destOptionalsCount];
|
|
srcOptionals.erase(srcOptionals.begin() + destOptionalsCount,
|
|
srcOptionals.end());
|
|
}
|
|
}
|
|
|
|
// When performing a bridging operation, if the destination type
|
|
// is more optional than the source, we'll add extra optional injections
|
|
// at the end.
|
|
SmallVector<Type, 4> destOptionalInjections;
|
|
if (castKind == OptionalBindingsCastKind::Bridged &&
|
|
destOptionals.size() > srcOptionals.size()) {
|
|
// Remove the extra optionals from destOptionals, but keep them around
|
|
// separately so we can perform the injections on the final result of
|
|
// the cast.
|
|
auto cutPoint = destOptionals.end() - srcOptionals.size();
|
|
destOptionalInjections.append(destOptionals.begin(), cutPoint);
|
|
destOptionals.erase(destOptionals.begin(), cutPoint);
|
|
|
|
finalResultType = destOptionals.empty() ? destValueType
|
|
: destOptionals.front();
|
|
}
|
|
|
|
// Local function to add the optional injections to final result.
|
|
auto addFinalOptionalInjections = [&](Expr *result) {
|
|
for (auto destType : llvm::reverse(destOptionalInjections)) {
|
|
result =
|
|
cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, destType));
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
// There's nothing special to do if the operand isn't optional
|
|
// (or is insufficiently optional) and we don't need any bridging.
|
|
if (srcOptionals.empty()
|
|
|| (srcOptionals.size() < destOptionals.size() - destExtraOptionals)) {
|
|
Expr *result = buildInnerOperation(subExpr, finalResultType);
|
|
if (!result) return nullptr;
|
|
return addFinalOptionalInjections(result);
|
|
}
|
|
|
|
// The outermost N levels of optionals on the operand must all
|
|
// be present or the cast fails. The innermost M levels of
|
|
// optionals on the operand are reflected in the requested
|
|
// destination type, so we should map these nils into the result.
|
|
unsigned numRequiredOptionals =
|
|
srcOptionals.size() - (destOptionals.size() - destExtraOptionals);
|
|
|
|
// The number of OptionalEvaluationExprs between the point of the
|
|
// inner cast and the enclosing OptionalEvaluationExpr (exclusive)
|
|
// which represents failure for the entire operation.
|
|
unsigned failureDepth = destOptionals.size() - destExtraOptionals;
|
|
|
|
// Drill down on the operand until it's non-optional.
|
|
SourceLoc fakeQuestionLoc = subExpr->getEndLoc();
|
|
for (unsigned i : indices(srcOptionals)) {
|
|
Type valueType =
|
|
(i + 1 == srcOptionals.size() ? srcType : srcOptionals[i+1]);
|
|
|
|
// As we move into the range of mapped optionals, start
|
|
// lowering the depth.
|
|
unsigned depth = failureDepth;
|
|
if (i >= numRequiredOptionals) {
|
|
depth -= (i - numRequiredOptionals) + 1;
|
|
} else if (forceExtraSourceOptionals) {
|
|
// For a forced cast, force the required optionals.
|
|
subExpr = new (ctx) ForceValueExpr(subExpr, fakeQuestionLoc);
|
|
cs.setType(subExpr, valueType);
|
|
subExpr->setImplicit(true);
|
|
continue;
|
|
}
|
|
|
|
subExpr = cs.cacheType(new (ctx) BindOptionalExpr(
|
|
subExpr, fakeQuestionLoc, depth, valueType));
|
|
subExpr->setImplicit(true);
|
|
}
|
|
|
|
// If this is a conditional cast, the result type will always
|
|
// have at least one level of optional, which should become the
|
|
// type of the checked-cast expression.
|
|
Expr *result;
|
|
if (castKind == OptionalBindingsCastKind::Conditional) {
|
|
assert(!destOptionals.empty() &&
|
|
"result of checked cast is not an optional type");
|
|
result = buildInnerOperation(subExpr, destOptionals.back());
|
|
} else {
|
|
result = buildInnerOperation(subExpr, destValueType);
|
|
}
|
|
if (!result) return nullptr;
|
|
|
|
// If we're casting to an optional type, we need to capture the
|
|
// final M bindings.
|
|
|
|
if (destOptionals.size() > destExtraOptionals) {
|
|
if (castKind == OptionalBindingsCastKind::Conditional) {
|
|
// If the innermost cast fails, the entire expression fails. To
|
|
// get this behavior, we have to bind and then re-inject the result.
|
|
// (SILGen should know how to peephole this.)
|
|
result = cs.cacheType(new (ctx) BindOptionalExpr(
|
|
result, result->getEndLoc(), failureDepth, destValueType));
|
|
result->setImplicit(true);
|
|
}
|
|
|
|
for (unsigned i = destOptionals.size(); i != 0; --i) {
|
|
Type destType = destOptionals[i-1];
|
|
result =
|
|
cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, destType));
|
|
result =
|
|
cs.cacheType(new (ctx) OptionalEvaluationExpr(result, destType));
|
|
}
|
|
|
|
// Otherwise, we just need to capture the failure-depth binding.
|
|
} else if (!forceExtraSourceOptionals) {
|
|
result = cs.cacheType(
|
|
new (ctx) OptionalEvaluationExpr(result, finalResultType));
|
|
}
|
|
|
|
return addFinalOptionalInjections(result);
|
|
}
|
|
|
|
bool hasForcedOptionalResult(ExplicitCastExpr *expr) {
|
|
const auto *const TR = expr->getCastTypeRepr();
|
|
if (TR && TR->getKind() == TypeReprKind::ImplicitlyUnwrappedOptional) {
|
|
auto *locator = cs.getConstraintLocator(
|
|
expr, ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice);
|
|
return solution.getDisjunctionChoice(locator);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Expr *visitCoerceExpr(CoerceExpr *expr) {
|
|
auto *coerced = visitCoerceExprImpl(expr);
|
|
if (!coerced)
|
|
return nullptr;
|
|
|
|
// If we need to insert a force-unwrap for coercions of the form
|
|
// 'as T!', do so now.
|
|
if (hasForcedOptionalResult(expr))
|
|
coerced = forceUnwrapIUO(coerced);
|
|
|
|
return coerced;
|
|
}
|
|
|
|
Expr *visitCoerceExprImpl(CoerceExpr *expr) {
|
|
if (auto *castTypeRepr = expr->getCastTypeRepr()) {
|
|
// Simplify and update the type we're coercing to.
|
|
auto toType = simplifyType(cs.getType(castTypeRepr));
|
|
expr->setCastType(toType);
|
|
cs.setType(castTypeRepr, toType);
|
|
} else {
|
|
assert(expr->isImplicit());
|
|
assert(expr->getCastType());
|
|
}
|
|
|
|
cs.setType(expr, expr->getCastType());
|
|
|
|
// If this is a literal that got converted into constructor call
|
|
// lets put proper source information in place.
|
|
if (expr->isLiteralInit()) {
|
|
auto toType = expr->getCastType();
|
|
auto *literalInit = expr->getSubExpr();
|
|
if (auto *call = dyn_cast<CallExpr>(literalInit)) {
|
|
cs.forEachExpr(call->getFn(), [&](Expr *subExpr) -> Expr * {
|
|
auto *TE = dyn_cast<TypeExpr>(subExpr);
|
|
if (!TE)
|
|
return subExpr;
|
|
|
|
auto type = cs.getInstanceType(TE);
|
|
|
|
assert(!type->hasError());
|
|
|
|
if (!type->isEqual(toType))
|
|
return subExpr;
|
|
|
|
return cs.cacheType(TypeExpr::createImplicitHack(
|
|
expr->getLoc(), toType, ctx));
|
|
});
|
|
}
|
|
|
|
if (auto *literal = dyn_cast<NumberLiteralExpr>(literalInit)) {
|
|
literal->setExplicitConversion();
|
|
} else {
|
|
literalInit->setImplicit(false);
|
|
}
|
|
|
|
// Keep the coercion around, because it contains the source range
|
|
// for the original constructor call.
|
|
return expr;
|
|
}
|
|
|
|
// Turn the subexpression into an rvalue.
|
|
auto rvalueSub = cs.coerceToRValue(expr->getSubExpr());
|
|
expr->setSubExpr(rvalueSub);
|
|
|
|
// If we weren't explicitly told by the caller which disjunction choice,
|
|
// get it from the solution to determine whether we've picked a coercion
|
|
// or a bridging conversion.
|
|
auto *locator =
|
|
cs.getConstraintLocator(expr, ConstraintLocator::CoercionOperand);
|
|
auto choice = solution.getDisjunctionChoice(locator);
|
|
|
|
// Handle the coercion/bridging of the underlying subexpression, where
|
|
// optionality has been removed.
|
|
if (choice == 0) {
|
|
// Convert the subexpression.
|
|
Expr *sub = expr->getSubExpr();
|
|
auto subLoc =
|
|
cs.getConstraintLocator(sub, ConstraintLocator::CoercionOperand);
|
|
sub = solution.coerceToType(sub, expr->getCastType(), subLoc);
|
|
if (!sub)
|
|
return nullptr;
|
|
|
|
expr->setSubExpr(sub);
|
|
return expr;
|
|
}
|
|
|
|
// Bridging conversion.
|
|
assert(choice == 1 && "should be bridging");
|
|
|
|
// Handle optional bindings.
|
|
Expr *sub = handleOptionalBindings(
|
|
expr->getSubExpr(), expr->getCastType(),
|
|
OptionalBindingsCastKind::Bridged,
|
|
[&](Expr *sub, Type toInstanceType) {
|
|
return buildObjCBridgeExpr(sub, toInstanceType, locator);
|
|
});
|
|
|
|
if (!sub)
|
|
return nullptr;
|
|
|
|
expr->setSubExpr(sub);
|
|
return expr;
|
|
}
|
|
|
|
// Rewrite ForcedCheckedCastExpr based on what the solver computed.
|
|
Expr *visitForcedCheckedCastExpr(ForcedCheckedCastExpr *expr) {
|
|
// The subexpression is always an rvalue.
|
|
auto sub = cs.coerceToRValue(expr->getSubExpr());
|
|
expr->setSubExpr(sub);
|
|
|
|
const auto fromType = cs.getType(sub);
|
|
|
|
Type toType;
|
|
SourceRange castTypeRange;
|
|
|
|
// Simplify and update the type we're casting to.
|
|
if (auto *const castTypeRepr = expr->getCastTypeRepr()) {
|
|
toType = simplifyType(cs.getType(castTypeRepr));
|
|
castTypeRange = castTypeRepr->getSourceRange();
|
|
|
|
cs.setType(castTypeRepr, toType);
|
|
expr->setCastType(toType);
|
|
} else {
|
|
assert(expr->isImplicit());
|
|
assert(expr->getCastType());
|
|
|
|
toType = expr->getCastType();
|
|
}
|
|
|
|
if (hasForcedOptionalResult(expr))
|
|
toType = toType->getOptionalObjectType();
|
|
|
|
const auto castKind = TypeChecker::typeCheckCheckedCast(
|
|
fromType, toType, CheckedCastContextKind::ForcedCast, dc);
|
|
switch (castKind) {
|
|
/// Invalid cast.
|
|
case CheckedCastKind::Unresolved:
|
|
if (expr->isImplicit())
|
|
return nullptr;
|
|
|
|
expr->setCastKind(CheckedCastKind::ValueCast);
|
|
break;
|
|
case CheckedCastKind::Coercion:
|
|
case CheckedCastKind::BridgingCoercion: {
|
|
expr->setCastKind(castKind);
|
|
cs.setType(expr, toType);
|
|
return expr;
|
|
}
|
|
|
|
// Valid casts.
|
|
case CheckedCastKind::ArrayDowncast:
|
|
case CheckedCastKind::DictionaryDowncast:
|
|
case CheckedCastKind::SetDowncast:
|
|
case CheckedCastKind::ValueCast:
|
|
expr->setCastKind(castKind);
|
|
break;
|
|
}
|
|
|
|
return handleOptionalBindingsForCast(expr, simplifyType(cs.getType(expr)),
|
|
OptionalBindingsCastKind::Forced);
|
|
}
|
|
|
|
Expr *visitConditionalCheckedCastExpr(ConditionalCheckedCastExpr *expr) {
|
|
auto *const castTypeRepr = expr->getCastTypeRepr();
|
|
|
|
// If there is no type repr, it means this is implicit cast which
|
|
// should have a type set.
|
|
if (!castTypeRepr) {
|
|
assert(expr->isImplicit());
|
|
assert(expr->getCastType());
|
|
|
|
auto sub = cs.coerceToRValue(expr->getSubExpr());
|
|
expr->setSubExpr(sub);
|
|
|
|
return expr;
|
|
}
|
|
|
|
// Simplify and update the type we're casting to.
|
|
const auto toType = simplifyType(cs.getType(castTypeRepr));
|
|
expr->setCastType(toType);
|
|
|
|
cs.setType(castTypeRepr, toType);
|
|
|
|
// If we need to insert a force-unwrap for coercions of the form
|
|
// 'as! T!', do so now.
|
|
if (hasForcedOptionalResult(expr)) {
|
|
auto *coerced = handleConditionalCheckedCastExpr(expr, castTypeRepr);
|
|
if (!coerced)
|
|
return nullptr;
|
|
|
|
return forceUnwrapIUO(coerced);
|
|
}
|
|
|
|
return handleConditionalCheckedCastExpr(expr, castTypeRepr);
|
|
}
|
|
|
|
Expr *handleConditionalCheckedCastExpr(ConditionalCheckedCastExpr *expr,
|
|
TypeRepr *castTypeRepr) {
|
|
assert(castTypeRepr &&
|
|
"cast requires TypeRepr; implicit casts are superfluous");
|
|
|
|
// The subexpression is always an rvalue.
|
|
auto sub = cs.coerceToRValue(expr->getSubExpr());
|
|
expr->setSubExpr(sub);
|
|
|
|
// Simplify and update the type we're casting to.
|
|
const auto fromType = cs.getType(sub);
|
|
const auto toType = expr->getCastType();
|
|
|
|
auto castKind = TypeChecker::typeCheckCheckedCast(
|
|
fromType, toType, CheckedCastContextKind::ConditionalCast, dc);
|
|
switch (castKind) {
|
|
// Invalid cast.
|
|
case CheckedCastKind::Unresolved:
|
|
expr->setCastKind(CheckedCastKind::ValueCast);
|
|
break;
|
|
|
|
case CheckedCastKind::Coercion:
|
|
case CheckedCastKind::BridgingCoercion: {
|
|
expr->setCastKind(castKind);
|
|
cs.setType(expr, OptionalType::get(toType));
|
|
return expr;
|
|
}
|
|
|
|
// Valid casts.
|
|
case CheckedCastKind::ArrayDowncast:
|
|
case CheckedCastKind::DictionaryDowncast:
|
|
case CheckedCastKind::SetDowncast:
|
|
case CheckedCastKind::ValueCast:
|
|
expr->setCastKind(castKind);
|
|
break;
|
|
}
|
|
|
|
return handleOptionalBindingsForCast(expr, simplifyType(cs.getType(expr)),
|
|
OptionalBindingsCastKind::Conditional);
|
|
}
|
|
|
|
Expr *visitAssignExpr(AssignExpr *expr) {
|
|
// Convert the source to the simplified destination type.
|
|
auto destTy = simplifyType(cs.getType(expr->getDest()));
|
|
// Conversion is recorded as anchored on an assignment itself by
|
|
// constraint generator and that has to be preserved here in case
|
|
// anything depends on the locator (i.e. Double<->CGFloat implicit
|
|
// conversion).
|
|
Expr *src = coerceToType(expr->getSrc(), destTy->getRValueType(),
|
|
cs.getConstraintLocator(expr));
|
|
if (!src)
|
|
return nullptr;
|
|
|
|
expr->setSrc(src);
|
|
|
|
if (!SuppressDiagnostics) {
|
|
// If we're performing an assignment to a weak or unowned variable from
|
|
// a constructor call, emit a warning that the instance will be
|
|
// immediately deallocated.
|
|
diagnoseUnownedImmediateDeallocation(ctx, expr);
|
|
}
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitDiscardAssignmentExpr(DiscardAssignmentExpr *expr) {
|
|
return simplifyExprType(expr);
|
|
}
|
|
|
|
Expr *visitUnresolvedPatternExpr(UnresolvedPatternExpr *expr) {
|
|
llvm_unreachable("Should have diagnosed");
|
|
}
|
|
|
|
Expr *visitBindOptionalExpr(BindOptionalExpr *expr) {
|
|
return simplifyExprType(expr);
|
|
}
|
|
|
|
Expr *visitOptionalEvaluationExpr(OptionalEvaluationExpr *expr) {
|
|
Type optType = simplifyType(cs.getType(expr));
|
|
|
|
// If this is an optional chain that isn't chaining anything, and if the
|
|
// subexpression is already optional (not IUO), then this is a noop:
|
|
// reject it. This avoids confusion of the model (where the programmer
|
|
// thought it was doing something) and keeps pointless ?'s out of the
|
|
// code.
|
|
if (!SuppressDiagnostics) {
|
|
auto &de = ctx.Diags;
|
|
if (auto *Bind = dyn_cast<BindOptionalExpr>(
|
|
expr->getSubExpr()->getSemanticsProvidingExpr())) {
|
|
if (cs.getType(Bind->getSubExpr())->isEqual(optType)) {
|
|
de.diagnose(expr->getLoc(), diag::optional_chain_noop, optType)
|
|
.fixItRemove(Bind->getQuestionLoc());
|
|
} else {
|
|
de.diagnose(expr->getLoc(), diag::optional_chain_isnt_chaining);
|
|
}
|
|
}
|
|
}
|
|
|
|
Expr *subExpr = coerceToType(expr->getSubExpr(), optType,
|
|
cs.getConstraintLocator(expr));
|
|
if (!subExpr) return nullptr;
|
|
|
|
expr->setSubExpr(subExpr);
|
|
cs.setType(expr, optType);
|
|
return expr;
|
|
}
|
|
|
|
Expr *visitForceValueExpr(ForceValueExpr *expr) {
|
|
return simplifyExprType(expr);
|
|
}
|
|
|
|
Expr *visitOpenExistentialExpr(OpenExistentialExpr *expr) {
|
|
llvm_unreachable("Already type-checked");
|
|
}
|
|
|
|
Expr *visitMakeTemporarilyEscapableExpr(MakeTemporarilyEscapableExpr *expr){
|
|
llvm_unreachable("Already type-checked");
|
|
}
|
|
|
|
Expr *visitKeyPathApplicationExpr(KeyPathApplicationExpr *expr){
|
|
// This should already be type-checked, but we may have had to re-
|
|
// check it for failure diagnosis.
|
|
return simplifyExprType(expr);
|
|
}
|
|
|
|
Expr *visitEnumIsCaseExpr(EnumIsCaseExpr *expr) {
|
|
// Should already be type-checked.
|
|
return simplifyExprType(expr);
|
|
}
|
|
|
|
Expr *visitLazyInitializerExpr(LazyInitializerExpr *expr) {
|
|
llvm_unreachable("Already type-checked");
|
|
}
|
|
|
|
Expr *visitEditorPlaceholderExpr(EditorPlaceholderExpr *E) {
|
|
simplifyExprType(E);
|
|
auto valueType = cs.getType(E);
|
|
|
|
// Synthesize a call to _undefined() of appropriate type.
|
|
FuncDecl *undefinedDecl = ctx.getUndefined();
|
|
if (!undefinedDecl) {
|
|
ctx.Diags.diagnose(E->getLoc(), diag::missing_undefined_runtime);
|
|
return nullptr;
|
|
}
|
|
DeclRefExpr *fnRef = new (ctx) DeclRefExpr(undefinedDecl, DeclNameLoc(),
|
|
/*Implicit=*/true);
|
|
fnRef->setFunctionRefInfo(FunctionRefInfo::singleBaseNameApply());
|
|
|
|
StringRef msg = "attempt to evaluate editor placeholder";
|
|
Expr *argExpr = new (ctx) StringLiteralExpr(msg, E->getLoc(),
|
|
/*implicit*/true);
|
|
|
|
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {argExpr});
|
|
Expr *callExpr = CallExpr::createImplicit(ctx, fnRef, argList);
|
|
|
|
auto resultTy = TypeChecker::typeCheckExpression(
|
|
callExpr, dc, /*contextualInfo=*/{valueType, CTP_CannotFail});
|
|
assert(resultTy && "Conversion cannot fail!");
|
|
(void)resultTy;
|
|
|
|
cs.cacheExprTypes(callExpr);
|
|
E->setSemanticExpr(callExpr);
|
|
return E;
|
|
}
|
|
|
|
Expr *visitObjCSelectorExpr(ObjCSelectorExpr *E) {
|
|
// Dig out the reference to a declaration.
|
|
Expr *subExpr = E->getSubExpr();
|
|
ValueDecl *foundDecl = nullptr;
|
|
while (subExpr) {
|
|
// Declaration reference.
|
|
if (auto declRef = dyn_cast<DeclRefExpr>(subExpr)) {
|
|
foundDecl = declRef->getDecl();
|
|
break;
|
|
}
|
|
|
|
// Constructor reference.
|
|
if (auto ctorRef = dyn_cast<OtherConstructorDeclRefExpr>(subExpr)) {
|
|
foundDecl = ctorRef->getDecl();
|
|
break;
|
|
}
|
|
|
|
// Member reference.
|
|
if (auto memberRef = dyn_cast<MemberRefExpr>(subExpr)) {
|
|
foundDecl = memberRef->getMember().getDecl();
|
|
break;
|
|
}
|
|
|
|
// Dynamic member reference.
|
|
if (auto dynMemberRef = dyn_cast<DynamicMemberRefExpr>(subExpr)) {
|
|
foundDecl = dynMemberRef->getMember().getDecl();
|
|
break;
|
|
}
|
|
|
|
// Look through parentheses.
|
|
if (auto paren = dyn_cast<ParenExpr>(subExpr)) {
|
|
subExpr = paren->getSubExpr();
|
|
continue;
|
|
}
|
|
|
|
// Look through "a.b" to "b".
|
|
if (auto dotSyntax = dyn_cast<DotSyntaxBaseIgnoredExpr>(subExpr)) {
|
|
subExpr = dotSyntax->getRHS();
|
|
continue;
|
|
}
|
|
|
|
// Look through self-rebind expression.
|
|
if (auto rebindSelf = dyn_cast<RebindSelfInConstructorExpr>(subExpr)) {
|
|
subExpr = rebindSelf->getSubExpr();
|
|
continue;
|
|
}
|
|
|
|
// Look through optional binding within the monadic "?".
|
|
if (auto bind = dyn_cast<BindOptionalExpr>(subExpr)) {
|
|
subExpr = bind->getSubExpr();
|
|
continue;
|
|
}
|
|
|
|
// Look through optional evaluation of the monadic "?".
|
|
if (auto optEval = dyn_cast<OptionalEvaluationExpr>(subExpr)) {
|
|
subExpr = optEval->getSubExpr();
|
|
continue;
|
|
}
|
|
|
|
// Look through an implicit force-value.
|
|
if (auto force = dyn_cast<ForceValueExpr>(subExpr)) {
|
|
subExpr = force->getSubExpr();
|
|
continue;
|
|
}
|
|
|
|
// Look through implicit open-existential operations.
|
|
if (auto open = dyn_cast<OpenExistentialExpr>(subExpr)) {
|
|
if (open->isImplicit()) {
|
|
subExpr = open->getSubExpr();
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Look to the referenced member in a self-application.
|
|
if (auto selfApply = dyn_cast<SelfApplyExpr>(subExpr)) {
|
|
subExpr = selfApply->getFn();
|
|
continue;
|
|
}
|
|
|
|
// Look through implicit conversions.
|
|
if (auto conversion = dyn_cast<ImplicitConversionExpr>(subExpr)) {
|
|
subExpr = conversion->getSubExpr();
|
|
continue;
|
|
}
|
|
|
|
// Look through explicit coercions.
|
|
if (auto coercion = dyn_cast<CoerceExpr>(subExpr)) {
|
|
subExpr = coercion->getSubExpr();
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (!subExpr) return nullptr;
|
|
|
|
// If we didn't find any declaration at all, we're stuck.
|
|
auto &de = ctx.Diags;
|
|
if (!foundDecl) {
|
|
de.diagnose(E->getLoc(), diag::expr_selector_no_declaration)
|
|
.highlight(subExpr->getSourceRange());
|
|
return E;
|
|
}
|
|
|
|
// Check whether we found an entity that #selector could refer to.
|
|
// If we found a method or initializer, check it.
|
|
AbstractFunctionDecl *method = nullptr;
|
|
if (auto func = dyn_cast<AbstractFunctionDecl>(foundDecl)) {
|
|
// Methods and initializers.
|
|
|
|
// If this isn't a method, complain.
|
|
if (!func->getDeclContext()->isTypeContext()) {
|
|
de.diagnose(E->getLoc(), diag::expr_selector_not_method,
|
|
func->getDeclContext()->isModuleScopeContext(),
|
|
func)
|
|
.highlight(subExpr->getSourceRange());
|
|
de.diagnose(func, diag::decl_declared_here, func);
|
|
return E;
|
|
}
|
|
|
|
// Check that we requested a method.
|
|
switch (E->getSelectorKind()) {
|
|
case ObjCSelectorExpr::Method:
|
|
break;
|
|
|
|
case ObjCSelectorExpr::Getter:
|
|
case ObjCSelectorExpr::Setter:
|
|
// Complain that we cannot ask for the getter or setter of a
|
|
// method.
|
|
de.diagnose(E->getModifierLoc(),
|
|
diag::expr_selector_expected_property,
|
|
E->getSelectorKind() == ObjCSelectorExpr::Setter,
|
|
foundDecl)
|
|
.fixItRemoveChars(E->getModifierLoc(),
|
|
E->getSubExpr()->getStartLoc());
|
|
|
|
// Update the AST to reflect the fix.
|
|
E->overrideObjCSelectorKind(ObjCSelectorExpr::Method, SourceLoc());
|
|
break;
|
|
}
|
|
|
|
// Note the method we're referring to.
|
|
method = func;
|
|
} else if (auto var = dyn_cast<VarDecl>(foundDecl)) {
|
|
// Properties.
|
|
|
|
// If this isn't a property on a type, complain.
|
|
if (!var->getDeclContext()->isTypeContext()) {
|
|
de.diagnose(E->getLoc(), diag::expr_selector_not_property,
|
|
isa<ParamDecl>(var), var)
|
|
.highlight(subExpr->getSourceRange());
|
|
de.diagnose(var, diag::decl_declared_here, var);
|
|
return E;
|
|
}
|
|
|
|
// Check that we requested a property getter or setter.
|
|
switch (E->getSelectorKind()) {
|
|
case ObjCSelectorExpr::Method: {
|
|
bool isSettable = var->isSettable(dc) &&
|
|
var->isSetterAccessibleFrom(dc);
|
|
auto primaryDiag =
|
|
de.diagnose(E->getLoc(), diag::expr_selector_expected_method,
|
|
isSettable, var);
|
|
primaryDiag.highlight(subExpr->getSourceRange());
|
|
|
|
// The point at which we will insert the modifier.
|
|
SourceLoc modifierLoc = E->getSubExpr()->getStartLoc();
|
|
|
|
// If the property is settable, we don't know whether the
|
|
// user wanted the getter or setter. Provide notes for each.
|
|
if (isSettable) {
|
|
// Add notes for the getter and setter, respectively.
|
|
de.diagnose(modifierLoc, diag::expr_selector_add_modifier, false,
|
|
var)
|
|
.fixItInsert(modifierLoc, "getter: ");
|
|
de.diagnose(modifierLoc, diag::expr_selector_add_modifier, true,
|
|
var)
|
|
.fixItInsert(modifierLoc, "setter: ");
|
|
|
|
// Bail out now. We don't know what the user wanted, so
|
|
// don't fill in the details.
|
|
return E;
|
|
}
|
|
|
|
// The property is non-settable, so add "getter:".
|
|
primaryDiag.fixItInsert(modifierLoc, "getter: ");
|
|
E->overrideObjCSelectorKind(ObjCSelectorExpr::Getter, modifierLoc);
|
|
method = var->getOpaqueAccessor(AccessorKind::Get);
|
|
break;
|
|
}
|
|
|
|
case ObjCSelectorExpr::Getter:
|
|
method = var->getOpaqueAccessor(AccessorKind::Get);
|
|
break;
|
|
|
|
case ObjCSelectorExpr::Setter:
|
|
// Make sure we actually have a setter.
|
|
if (!var->isSettable(dc)) {
|
|
de.diagnose(E->getLoc(), diag::expr_selector_property_not_settable,
|
|
var);
|
|
de.diagnose(var, diag::decl_declared_here, var);
|
|
return E;
|
|
}
|
|
|
|
// Make sure the setter is accessible.
|
|
if (!var->isSetterAccessibleFrom(dc)) {
|
|
de.diagnose(E->getLoc(),
|
|
diag::expr_selector_property_setter_inaccessible, var);
|
|
de.diagnose(var, diag::decl_declared_here, var);
|
|
return E;
|
|
}
|
|
|
|
method = var->getOpaqueAccessor(AccessorKind::Set);
|
|
break;
|
|
}
|
|
} else {
|
|
// Cannot reference with #selector.
|
|
de.diagnose(E->getLoc(), diag::expr_selector_no_declaration)
|
|
.highlight(subExpr->getSourceRange());
|
|
de.diagnose(foundDecl, diag::decl_declared_here, foundDecl);
|
|
return E;
|
|
}
|
|
assert(method && "Didn't find a method?");
|
|
|
|
// The declaration we found must be exposed to Objective-C.
|
|
if (!method->isObjC()) {
|
|
// If the method declaration lies in a protocol and we're providing
|
|
// a default implementation of the method through a protocol extension
|
|
// and using it as a selector, then bail out as adding @objc to the
|
|
// protocol might not be the right thing to do and could lead to
|
|
// problems.
|
|
if (auto protocolDecl = dyn_cast<ProtocolDecl>(foundDecl->getDeclContext())) {
|
|
de.diagnose(E->getLoc(), diag::expr_selector_cannot_be_used,
|
|
foundDecl, protocolDecl);
|
|
return E;
|
|
}
|
|
|
|
de.diagnose(E->getLoc(), diag::expr_selector_not_objc, foundDecl)
|
|
.highlight(subExpr->getSourceRange());
|
|
de.diagnose(foundDecl, diag::make_decl_objc, foundDecl)
|
|
.fixItInsert(foundDecl->getAttributeInsertionLoc(false), "@objc ");
|
|
return E;
|
|
}
|
|
|
|
// Note which method we're referencing.
|
|
E->setMethod(method);
|
|
return E;
|
|
}
|
|
|
|
Expr *visitKeyPathExpr(KeyPathExpr *E) {
|
|
if (E->isObjC()) {
|
|
cs.setType(E, cs.getType(E->getObjCStringLiteralExpr()));
|
|
return E;
|
|
}
|
|
|
|
simplifyExprType(E);
|
|
|
|
if (cs.getType(E)->hasError())
|
|
return E;
|
|
|
|
// If a component is already resolved, then all of them should be
|
|
// resolved, and we can let the expression be. This might happen when
|
|
// re-checking a failed system for diagnostics.
|
|
if (!E->getComponents().empty()
|
|
&& E->getComponents().front().isResolved()) {
|
|
assert([&]{
|
|
for (auto &c : E->getComponents())
|
|
if (!c.isResolved())
|
|
return false;
|
|
return true;
|
|
}());
|
|
return E;
|
|
}
|
|
|
|
SmallVector<KeyPathExpr::Component, 4> resolvedComponents;
|
|
|
|
// Resolve each of the components.
|
|
bool didOptionalChain = false;
|
|
bool isFunctionType = false;
|
|
auto baseTy = cs.simplifyType(solution.getKeyPathRootType(E));
|
|
Type leafTy;
|
|
Type exprType = cs.getType(E);
|
|
if (auto fnTy = exprType->getAs<FunctionType>()) {
|
|
leafTy = fnTy->getResult();
|
|
isFunctionType = true;
|
|
} else if (auto *existential = exprType->getAs<ExistentialType>()) {
|
|
auto layout = existential->getExistentialLayout();
|
|
auto keyPathTy = layout.explicitSuperclass->castTo<BoundGenericType>();
|
|
leafTy = keyPathTy->getGenericArgs()[1];
|
|
} else {
|
|
auto keyPathTy = exprType->castTo<BoundGenericType>();
|
|
leafTy = keyPathTy->getGenericArgs()[1];
|
|
}
|
|
|
|
// Track the type of the current component. Once we finish projecting
|
|
// through each component of the key path, we should reach the leafTy.
|
|
auto componentTy = baseTy;
|
|
for (unsigned i : indices(E->getComponents())) {
|
|
auto &origComponent = E->getMutableComponents()[i];
|
|
|
|
auto kind = origComponent.getKind();
|
|
auto componentLocator =
|
|
cs.getConstraintLocator(E, LocatorPathElt::KeyPathComponent(i));
|
|
|
|
// Get a locator such that it includes any additional elements to point
|
|
// to the component's callee, e.g a SubscriptMember for a subscript
|
|
// component.
|
|
auto calleeLoc = cs.getCalleeLocator(componentLocator);
|
|
|
|
bool isDynamicMember = false;
|
|
// If this is an unresolved link, make sure we resolved it.
|
|
if (kind == KeyPathExpr::Component::Kind::UnresolvedMember ||
|
|
kind == KeyPathExpr::Component::Kind::UnresolvedSubscript) {
|
|
auto foundDecl = solution.getOverloadChoice(calleeLoc);
|
|
isDynamicMember = foundDecl.choice.isAnyDynamicMemberLookup();
|
|
|
|
// If this was a @dynamicMemberLookup property, then we actually
|
|
// form a subscript reference, so switch the kind.
|
|
if (isDynamicMember) {
|
|
kind = KeyPathExpr::Component::Kind::UnresolvedSubscript;
|
|
}
|
|
}
|
|
|
|
switch (kind) {
|
|
case KeyPathExpr::Component::Kind::UnresolvedMember: {
|
|
buildKeyPathMemberComponent(solution.getOverloadChoice(calleeLoc),
|
|
origComponent.getLoc(), calleeLoc,
|
|
resolvedComponents);
|
|
break;
|
|
}
|
|
case KeyPathExpr::Component::Kind::UnresolvedSubscript: {
|
|
buildKeyPathSubscriptComponent(
|
|
solution.getOverloadChoice(calleeLoc), origComponent.getLoc(),
|
|
origComponent.getArgs(), componentLocator, resolvedComponents);
|
|
break;
|
|
}
|
|
case KeyPathExpr::Component::Kind::UnresolvedApply: {
|
|
// Get the calleeLoc of the member that requires application
|
|
// resolution of its arguments. Keypath methods are a member component
|
|
// followed by an unapplied component, so use the member component to
|
|
// find the overload.
|
|
auto memberComponentLoc = cs.getConstraintLocator(
|
|
E, LocatorPathElt::KeyPathComponent(i - 1));
|
|
|
|
buildKeyPathApplyComponent(
|
|
solution.getOverloadChoice(memberComponentLoc),
|
|
origComponent.getLoc(), origComponent.getArgs(), componentLocator,
|
|
memberComponentLoc, resolvedComponents);
|
|
break;
|
|
}
|
|
case KeyPathExpr::Component::Kind::OptionalChain: {
|
|
didOptionalChain = true;
|
|
// Chaining always forces the element to be an rvalue.
|
|
auto objectTy =
|
|
componentTy->getWithoutSpecifierType()->getOptionalObjectType();
|
|
assert(objectTy);
|
|
|
|
auto loc = origComponent.getLoc();
|
|
resolvedComponents.push_back(
|
|
KeyPathExpr::Component::forOptionalChain(objectTy, loc));
|
|
break;
|
|
}
|
|
case KeyPathExpr::Component::Kind::OptionalForce: {
|
|
// Handle force optional when it is the first component e.g.
|
|
// \String?.!.count
|
|
if (resolvedComponents.empty()) {
|
|
auto loc = origComponent.getLoc();
|
|
auto objectTy = componentTy->getOptionalObjectType();
|
|
assert(objectTy);
|
|
|
|
resolvedComponents.push_back(
|
|
KeyPathExpr::Component::forOptionalForce(objectTy, loc));
|
|
} else {
|
|
buildKeyPathOptionalForceComponent(resolvedComponents);
|
|
}
|
|
break;
|
|
}
|
|
case KeyPathExpr::Component::Kind::Identity: {
|
|
auto component = origComponent;
|
|
component.setComponentType(componentTy);
|
|
resolvedComponents.push_back(component);
|
|
break;
|
|
}
|
|
case KeyPathExpr::Component::Kind::CodeCompletion:
|
|
llvm_unreachable("solver-based completion shouldn't do CSApply");
|
|
break;
|
|
case KeyPathExpr::Component::Kind::Invalid:
|
|
llvm_unreachable("should have been diagnosed");
|
|
break;
|
|
case KeyPathExpr::Component::Kind::Member:
|
|
case KeyPathExpr::Component::Kind::Subscript:
|
|
case KeyPathExpr::Component::Kind::Apply:
|
|
case KeyPathExpr::Component::Kind::OptionalWrap:
|
|
case KeyPathExpr::Component::Kind::TupleElement:
|
|
llvm_unreachable("already resolved");
|
|
break;
|
|
case KeyPathExpr::Component::Kind::DictionaryKey:
|
|
llvm_unreachable("DictionaryKey only valid in #keyPath");
|
|
break;
|
|
}
|
|
componentTy = resolvedComponents.back().getComponentType();
|
|
}
|
|
|
|
// Wrap a non-optional result if there was chaining involved.
|
|
if (didOptionalChain &&
|
|
!componentTy->getWithoutSpecifierType()->isEqual(leafTy)) {
|
|
auto component = KeyPathExpr::Component::forOptionalWrap(leafTy);
|
|
resolvedComponents.push_back(component);
|
|
// Optional chaining forces the component to be r-value.
|
|
componentTy = OptionalType::get(componentTy->getWithoutSpecifierType());
|
|
}
|
|
|
|
// Set the resolved components, and cache their types.
|
|
E->setComponents(ctx, resolvedComponents);
|
|
cs.cacheExprTypes(E);
|
|
|
|
// See whether there's an equivalent ObjC key path string we can produce
|
|
// for interop purposes.
|
|
checkAndSetObjCKeyPathString(E);
|
|
|
|
if (!isFunctionType)
|
|
return E;
|
|
|
|
// If we've gotten here, the user has used key path literal syntax to form
|
|
// a closure. The type checker has given E a function type to indicate
|
|
// this.
|
|
//
|
|
// Since functions support more conversions than generic types, we may
|
|
// have ended up with a type of (baseTy) -> leafTy, where the actual type
|
|
// of the key path is some subclass of KeyPath<baseTy, componentTy>, and
|
|
// with componentTy: leafTy.
|
|
//
|
|
// We're going to change E's type to KeyPath<baseTy, componentTy> and
|
|
// then wrap it in a larger closure expression which we will convert to
|
|
// appropriate type.
|
|
|
|
auto kpResultTy = componentTy->getWithoutSpecifierType();
|
|
|
|
// Compute KeyPath<baseTy, leafTy> and set E's type back to it.
|
|
auto kpDecl = ctx.getKeyPathDecl();
|
|
auto keyPathTy =
|
|
BoundGenericType::get(kpDecl, nullptr, { baseTy, kpResultTy });
|
|
E->setType(keyPathTy);
|
|
cs.cacheType(E);
|
|
|
|
// To ensure side effects of the key path expression (mainly indices in
|
|
// subscripts) are only evaluated once, we use a capture list to evaluate
|
|
// the key path immediately and capture it in the function value created.
|
|
// The result looks like:
|
|
//
|
|
// return "{ [$kp$ = \(E)] in $0[keyPath: $kp$] }"
|
|
|
|
FunctionType::ExtInfo closureInfo;
|
|
auto closureTy =
|
|
FunctionType::get({FunctionType::Param(baseTy)}, kpResultTy,
|
|
closureInfo);
|
|
auto closure = new (ctx)
|
|
AutoClosureExpr(/*set body later*/nullptr, kpResultTy, dc);
|
|
|
|
auto param = new (ctx) ParamDecl(
|
|
SourceLoc(),
|
|
/*argument label*/ SourceLoc(), Identifier(),
|
|
/*parameter name*/ SourceLoc(), ctx.getIdentifier("$0"), closure);
|
|
param->setInterfaceType(baseTy->mapTypeOutOfContext());
|
|
param->setSpecifier(ParamSpecifier::Default);
|
|
param->setImplicit();
|
|
|
|
auto params = ParameterList::create(ctx, SourceLoc(),
|
|
param, SourceLoc());
|
|
|
|
closure->setParameterList(params);
|
|
|
|
// The capture list.
|
|
VarDecl *outerParam = new (ctx) VarDecl(/*static*/ false,
|
|
VarDecl::Introducer::Let,
|
|
SourceLoc(),
|
|
ctx.getIdentifier("$kp$"),
|
|
dc);
|
|
outerParam->setImplicit();
|
|
outerParam->setInterfaceType(keyPathTy->mapTypeOutOfContext());
|
|
|
|
auto *outerParamPat =
|
|
NamedPattern::createImplicit(ctx, outerParam, keyPathTy);
|
|
|
|
solution.setExprTypes(E);
|
|
auto *outerParamDecl = PatternBindingDecl::createImplicit(
|
|
ctx, StaticSpellingKind::None, outerParamPat, E, dc);
|
|
|
|
auto outerParamCapture = CaptureListEntry(outerParamDecl);
|
|
auto captureExpr = CaptureListExpr::create(ctx, outerParamCapture,
|
|
closure);
|
|
captureExpr->setImplicit();
|
|
|
|
// let paramRef = "$0"
|
|
auto *paramRef = new (ctx)
|
|
DeclRefExpr(param, DeclNameLoc(E->getLoc()), /*Implicit=*/true);
|
|
paramRef->setType(baseTy);
|
|
cs.cacheType(paramRef);
|
|
|
|
// let outerParamRef = "$kp$"
|
|
auto outerParamRef = new (ctx)
|
|
DeclRefExpr(outerParam, DeclNameLoc(E->getLoc()), /*Implicit=*/true);
|
|
outerParamRef->setType(keyPathTy);
|
|
cs.cacheType(outerParamRef);
|
|
|
|
// let application = "\(paramRef)[keyPath: \(outerParamRef)]"
|
|
auto *application = new (ctx)
|
|
KeyPathApplicationExpr(paramRef,
|
|
E->getStartLoc(), outerParamRef, E->getEndLoc(),
|
|
kpResultTy, /*implicit=*/true);
|
|
cs.cacheType(application);
|
|
|
|
// Finish up the inner closure.
|
|
closure->setParameterList(ParameterList::create(ctx, {param}));
|
|
closure->setBody(application);
|
|
closure->setType(closureTy);
|
|
cs.cacheType(closure);
|
|
|
|
captureExpr->setType(closureTy);
|
|
cs.cacheType(captureExpr);
|
|
|
|
return coerceToType(captureExpr, exprType, cs.getConstraintLocator(E));
|
|
}
|
|
|
|
void buildKeyPathOptionalForceComponent(
|
|
SmallVectorImpl<KeyPathExpr::Component> &components) {
|
|
assert(!components.empty());
|
|
|
|
// Unwrap the last component type, preserving @lvalue-ness.
|
|
auto optionalTy = components.back().getComponentType();
|
|
Type objectTy;
|
|
if (auto lvalue = optionalTy->getAs<LValueType>()) {
|
|
objectTy = lvalue->getObjectType()->getOptionalObjectType();
|
|
objectTy = LValueType::get(objectTy);
|
|
} else {
|
|
objectTy = optionalTy->getOptionalObjectType();
|
|
}
|
|
assert(objectTy);
|
|
|
|
auto loc = components.back().getLoc();
|
|
components.push_back(
|
|
KeyPathExpr::Component::forOptionalForce(objectTy, loc));
|
|
}
|
|
|
|
void buildKeyPathMemberComponent(
|
|
const SelectedOverload &overload, SourceLoc componentLoc,
|
|
ConstraintLocator *locator,
|
|
SmallVectorImpl<KeyPathExpr::Component> &components) {
|
|
auto resolvedTy = simplifyType(overload.adjustedOpenedType);
|
|
if (auto *member = overload.choice.getDeclOrNull()) {
|
|
if (auto varDecl = dyn_cast<VarDecl>(member)) {
|
|
// Key paths don't work with mutating-get properties.
|
|
assert(!varDecl->isGetterMutating());
|
|
}
|
|
|
|
// Compute the concrete reference to the member.
|
|
auto ref = resolveConcreteDeclRef(member, locator);
|
|
components.push_back(
|
|
KeyPathExpr::Component::forMember(ref, resolvedTy, componentLoc));
|
|
} else {
|
|
auto fieldIndex = overload.choice.getTupleIndex();
|
|
components.push_back(KeyPathExpr::Component::forTupleElement(
|
|
fieldIndex, resolvedTy, componentLoc));
|
|
}
|
|
|
|
auto unwrapCount =
|
|
getIUOForceUnwrapCount(locator, IUOReferenceKind::Value);
|
|
for (unsigned i = 0; i < unwrapCount; ++i)
|
|
buildKeyPathOptionalForceComponent(components);
|
|
}
|
|
|
|
void buildKeyPathSubscriptComponent(
|
|
const SelectedOverload &overload, SourceLoc componentLoc,
|
|
ArgumentList *args, ConstraintLocator *locator,
|
|
SmallVectorImpl<KeyPathExpr::Component> &components) {
|
|
auto subscript = cast<SubscriptDecl>(overload.choice.getDecl());
|
|
assert(!subscript->isGetterMutating());
|
|
auto memberLoc = cs.getCalleeLocator(locator);
|
|
|
|
// Compute substitutions to refer to the member.
|
|
auto ref = resolveConcreteDeclRef(subscript, memberLoc);
|
|
|
|
// If this is a @dynamicMemberLookup reference to resolve a property
|
|
// through the subscript(dynamicMember:) member, restore the
|
|
// openedType and origComponent to its full reference as if the user
|
|
// wrote out the subscript manually.
|
|
if (overload.choice.isAnyDynamicMemberLookup()) {
|
|
auto indexType = getTypeOfDynamicMemberIndex(overload);
|
|
Expr *argExpr = nullptr;
|
|
if (overload.choice.isKeyPathDynamicMemberLookup()) {
|
|
argExpr = buildKeyPathDynamicMemberArgExpr(indexType, componentLoc,
|
|
memberLoc);
|
|
} else {
|
|
auto fieldName = overload.choice.getName().getBaseIdentifier().str();
|
|
argExpr = buildDynamicMemberLookupArgExpr(fieldName, componentLoc,
|
|
indexType);
|
|
}
|
|
args = ArgumentList::forImplicitSingle(ctx, ctx.Id_dynamicMember,
|
|
argExpr);
|
|
// Record the implicit subscript expr's parameter bindings and matching
|
|
// direction as `coerceCallArguments` requires them.
|
|
solution.recordSingleArgMatchingChoice(locator);
|
|
}
|
|
|
|
auto subscriptType =
|
|
simplifyType(overload.adjustedOpenedType)->castTo<AnyFunctionType>();
|
|
auto resolvedTy = subscriptType->getResult();
|
|
|
|
// Coerce the indices to the type the subscript expects.
|
|
args = coerceCallArguments(
|
|
args, subscriptType, ref, /*applyExpr*/ nullptr,
|
|
cs.getConstraintLocator(locator, ConstraintLocator::ApplyArgument),
|
|
/*appliedPropertyWrappers*/ {});
|
|
|
|
// We need to be able to hash the captured index values in order for
|
|
// KeyPath itself to be hashable, so check that all of the subscript
|
|
// index components are hashable and collect their conformances here.
|
|
SmallVector<ProtocolConformanceRef, 4> conformances;
|
|
|
|
auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable);
|
|
|
|
auto fnType = overload.adjustedOpenedType->castTo<FunctionType>();
|
|
SmallVector<Identifier, 4> newLabels;
|
|
for (auto ¶m : fnType->getParams()) {
|
|
newLabels.push_back(param.getLabel());
|
|
|
|
auto indexType = simplifyType(param.getParameterType());
|
|
// Index type conformance to Hashable protocol has been
|
|
// verified by the solver, we just need to get it again
|
|
// with all of the generic parameters resolved.
|
|
auto hashableConformance = checkConformance(indexType, hashable);
|
|
assert(hashableConformance);
|
|
|
|
conformances.push_back(hashableConformance);
|
|
}
|
|
|
|
auto comp = KeyPathExpr::Component::forSubscript(
|
|
ctx, ref, args, resolvedTy, ctx.AllocateCopy(conformances));
|
|
components.push_back(comp);
|
|
|
|
auto unwrapCount =
|
|
getIUOForceUnwrapCount(memberLoc, IUOReferenceKind::ReturnValue);
|
|
for (unsigned i = 0; i < unwrapCount; ++i)
|
|
buildKeyPathOptionalForceComponent(components);
|
|
}
|
|
|
|
void buildKeyPathApplyComponent(
|
|
const SelectedOverload &overload, SourceLoc componentLoc,
|
|
ArgumentList *args, ConstraintLocator *applyLocator,
|
|
ConstraintLocator *memberLocator,
|
|
SmallVectorImpl<KeyPathExpr::Component> &components) {
|
|
auto &ctx = cs.getASTContext();
|
|
auto fnType = overload.adjustedOpenedType->castTo<FunctionType>();
|
|
|
|
// Compute substitutions to refer to the member.
|
|
auto ref =
|
|
resolveConcreteDeclRef(overload.choice.getDecl(), memberLocator);
|
|
|
|
// Coerce the args to the type the method/initializer expects.
|
|
args = coerceCallArguments(
|
|
args, fnType, ref, /*applyExpr*/ nullptr,
|
|
cs.getConstraintLocator(applyLocator,
|
|
ConstraintLocator::ApplyArgument),
|
|
/*appliedPropertyWrappers*/ {});
|
|
|
|
// We need to be able to hash the captured args in order for KeyPath
|
|
// itself to be hashable, so check that all of the arg components are
|
|
// hashable and collect their conformances here.
|
|
SmallVector<ProtocolConformanceRef, 4> conformances;
|
|
|
|
auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable);
|
|
|
|
SmallVector<Identifier, 4> newLabels;
|
|
for (auto ¶m : fnType->getParams()) {
|
|
newLabels.push_back(param.getLabel());
|
|
|
|
auto indexType = simplifyType(param.getParameterType());
|
|
// Arg type conformance to Hashable protocol has been
|
|
// verified by the solver, we just need to get it again
|
|
// with all of the generic parameters resolved.
|
|
auto hashableConformance = checkConformance(indexType, hashable);
|
|
assert(hashableConformance);
|
|
|
|
conformances.push_back(hashableConformance);
|
|
}
|
|
|
|
auto comp = KeyPathExpr::Component::forApply(
|
|
args, fnType->getResult(), ctx.AllocateCopy(conformances));
|
|
components.push_back(comp);
|
|
|
|
auto unwrapCount =
|
|
getIUOForceUnwrapCount(applyLocator, IUOReferenceKind::ReturnValue);
|
|
for (unsigned i = 0; i < unwrapCount; ++i)
|
|
buildKeyPathOptionalForceComponent(components);
|
|
}
|
|
|
|
Expr *visitCurrentContextIsolationExpr(CurrentContextIsolationExpr *E) {
|
|
E->setType(simplifyType(cs.getType(E)));
|
|
return E;
|
|
}
|
|
|
|
Expr *visitExtractFunctionIsolationExpr(ExtractFunctionIsolationExpr *E) {
|
|
llvm_unreachable("found ExtractFunctionIsolationExpr in CSApply");
|
|
}
|
|
|
|
Expr *visitKeyPathDotExpr(KeyPathDotExpr *E) {
|
|
llvm_unreachable("found KeyPathDotExpr in CSApply");
|
|
}
|
|
|
|
Expr *visitSingleValueStmtExpr(SingleValueStmtExpr *E) {
|
|
llvm_unreachable("Handled by the walker directly");
|
|
}
|
|
|
|
Expr *visitTapExpr(TapExpr *E) {
|
|
auto type = simplifyType(cs.getType(E));
|
|
|
|
E->getVar()->setInterfaceType(type->mapTypeOutOfContext());
|
|
|
|
cs.setType(E, type);
|
|
E->setType(type);
|
|
|
|
return E;
|
|
}
|
|
|
|
Expr *visitTypeJoinExpr(TypeJoinExpr *E) {
|
|
llvm_unreachable("already type-checked?");
|
|
}
|
|
|
|
Expr *visitMacroExpansionExpr(MacroExpansionExpr *E) {
|
|
auto expandedType = solution.simplifyType(solution.getType(E));
|
|
cs.setType(E, expandedType);
|
|
|
|
auto locator = cs.getConstraintLocator(E);
|
|
auto overload = solution.getOverloadChoice(locator);
|
|
|
|
auto macro = cast<MacroDecl>(overload.choice.getDecl());
|
|
ConcreteDeclRef macroRef = resolveConcreteDeclRef(macro, locator);
|
|
E->setMacroRef(macroRef);
|
|
E->setType(expandedType);
|
|
|
|
auto fnType =
|
|
simplifyType(overload.adjustedOpenedType)->castTo<FunctionType>();
|
|
|
|
auto newArgs = coerceCallArguments(
|
|
E->getArgs(), fnType, macroRef, /*applyExpr=*/nullptr,
|
|
cs.getConstraintLocator(E, ConstraintLocator::ApplyArgument),
|
|
solution.getAppliedPropertyWrappers(E));
|
|
if (!newArgs)
|
|
return nullptr;
|
|
|
|
E->setArgs(newArgs);
|
|
|
|
// FIXME: Expansion should be lazy.
|
|
// i.e. 'ExpandMacroExpansionExprRequest' should be sinked into
|
|
// 'getRewritten()', and performed on-demand. Unfortunately that requires
|
|
// auditing every ASTWalker's `getMacroWalkingBehavior` since
|
|
// `MacroWalking::Expansion` does not actually kick expansion.
|
|
if (!cs.Options.contains(ConstraintSystemFlags::DisableMacroExpansions) &&
|
|
// Do not expand macros inside macro arguments. For example for
|
|
// '#stringify(#assert(foo))' when typechecking `#assert(foo)`,
|
|
// we don't want to expand it.
|
|
llvm::none_of(llvm::ArrayRef(ExprStack).drop_back(1),
|
|
[](Expr *E) { return isa<MacroExpansionExpr>(E); })) {
|
|
// We need to delay the expansion until we're done applying the solution
|
|
// since running MiscDiagnostics on the expansion may walk up and query
|
|
// the type of a parent closure, e.g `diagnoseImplicitSelfUseInClosure`.
|
|
addMacroToExpand(E);
|
|
}
|
|
|
|
cs.cacheExprTypes(E);
|
|
return E;
|
|
}
|
|
|
|
/// Interface for ExprWalker
|
|
void walkToExprPre(Expr *expr) {
|
|
// If we have an apply, make a note of its callee locator prior to
|
|
// rewriting.
|
|
if (auto *apply = dyn_cast<ApplyExpr>(expr)) {
|
|
auto *calleeLoc = cs.getCalleeLocator(cs.getConstraintLocator(expr));
|
|
CalleeLocators[apply] = calleeLoc;
|
|
}
|
|
ExprStack.push_back(expr);
|
|
}
|
|
|
|
Expr *walkToExprPost(Expr *expr) {
|
|
Expr *result = visit(expr);
|
|
|
|
assert(expr == ExprStack.back());
|
|
ExprStack.pop_back();
|
|
|
|
return result;
|
|
}
|
|
|
|
void finalize() {
|
|
assert(ExprStack.empty());
|
|
assert(OpenedExistentials.empty());
|
|
|
|
// Look at all of the suspicious optional injections
|
|
for (auto injection : SuspiciousOptionalInjections) {
|
|
if (auto *cast = findForcedDowncast(ctx, injection->getSubExpr())) {
|
|
if (!isa<ParenExpr>(injection->getSubExpr())) {
|
|
ctx.Diags.diagnose(
|
|
injection->getLoc(), diag::inject_forced_downcast,
|
|
cs.getType(injection->getSubExpr())->getRValueType());
|
|
auto exclaimLoc = cast->getExclaimLoc();
|
|
ctx.Diags
|
|
.diagnose(exclaimLoc, diag::forced_to_conditional_downcast,
|
|
cs.getType(injection)->getOptionalObjectType())
|
|
.fixItReplace(exclaimLoc, "?");
|
|
ctx.Diags
|
|
.diagnose(cast->getStartLoc(),
|
|
diag::silence_inject_forced_downcast)
|
|
.fixItInsert(cast->getStartLoc(), "(")
|
|
.fixItInsertAfter(cast->getEndLoc(), ")");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Diagnose the implicit coercions of noncopyable values that happen in
|
|
// a context where it isn't "obviously" consuming already.
|
|
for (auto *coercion : ConsumingCoercions) {
|
|
assert(coercion->isImplicit());
|
|
ctx.Diags
|
|
.diagnose(coercion->getLoc(),
|
|
diag::consume_expression_needed_for_cast,
|
|
cs.getType(coercion));
|
|
ctx.Diags
|
|
.diagnose(coercion->getLoc(),
|
|
diag::add_consume_to_silence)
|
|
.fixItInsert(coercion->getStartLoc(), "consume ");
|
|
}
|
|
|
|
// Type-check any local decls encountered.
|
|
for (auto *D : LocalDeclsToTypeCheck)
|
|
TypeChecker::typeCheckDecl(D);
|
|
|
|
// Expand any macros encountered.
|
|
// FIXME: Expansion should be lazy.
|
|
auto &eval = ctx.evaluator;
|
|
for (auto *E : MacrosToExpand) {
|
|
(void)evaluateOrDefault(eval, ExpandMacroExpansionExprRequest{E},
|
|
std::nullopt);
|
|
}
|
|
}
|
|
|
|
/// Diagnose an optional injection that is probably not what the
|
|
/// user wanted, because it comes from a forced downcast, or from an
|
|
/// implicitly consumed noncopyable type.
|
|
void diagnoseOptionalInjection(InjectIntoOptionalExpr *injection,
|
|
ConstraintLocatorBuilder locator) {
|
|
// Check whether we have a forced downcast.
|
|
if (findForcedDowncast(ctx, injection->getSubExpr()))
|
|
SuspiciousOptionalInjections.push_back(injection);
|
|
|
|
/// Check if it needs an explicit consume, due to this being a cast.
|
|
auto *module = dc->getParentModule();
|
|
auto origType = solution.getResolvedType(injection->getSubExpr());
|
|
if (willHaveConfusingConsumption(origType, locator, cs) &&
|
|
canAddExplicitConsume(solution, module, injection->getSubExpr()))
|
|
ConsumingCoercions.push_back(injection);
|
|
}
|
|
|
|
void diagnoseExistentialErasureOf(Expr *fromExpr, Expr *toExpr,
|
|
ConstraintLocatorBuilder locator) {
|
|
auto *module = dc->getParentModule();
|
|
auto fromType = solution.getResolvedType(fromExpr);
|
|
if (willHaveConfusingConsumption(fromType, locator, cs) &&
|
|
canAddExplicitConsume(solution, module, fromExpr)) {
|
|
ConsumingCoercions.push_back(toExpr);
|
|
}
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
ConcreteDeclRef
|
|
Solution::resolveLocatorToDecl(ConstraintLocator *locator) const {
|
|
// Get the callee locator without looking through applies, ensuring we only
|
|
// return a decl for a direct reference.
|
|
auto *calleeLoc =
|
|
constraintSystem->getCalleeLocator(locator, /*lookThroughApply*/ false);
|
|
auto overload = getOverloadChoiceIfAvailable(calleeLoc);
|
|
if (!overload)
|
|
return ConcreteDeclRef();
|
|
|
|
return resolveConcreteDeclRef(overload->choice.getDeclOrNull(), locator);
|
|
}
|
|
|
|
/// Returns the concrete callee which 'owns' the default argument at a given
|
|
/// index. This looks through inheritance for inherited default args.
|
|
static ConcreteDeclRef getDefaultArgOwner(ConcreteDeclRef owner,
|
|
unsigned index) {
|
|
auto *param = getParameterAt(owner, index);
|
|
assert(param);
|
|
if (param->getDefaultArgumentKind() == DefaultArgumentKind::Inherited) {
|
|
return getDefaultArgOwner(owner.getOverriddenDecl(), index);
|
|
}
|
|
return owner;
|
|
}
|
|
|
|
static bool canPeepholeTupleConversion(Expr *expr,
|
|
ArrayRef<unsigned> sources) {
|
|
if (!isa<TupleExpr>(expr))
|
|
return false;
|
|
|
|
for (unsigned i = 0, e = sources.size(); i != e; ++i) {
|
|
if (sources[i] != i)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Expr *ExprRewriter::coerceTupleToTuple(Expr *expr,
|
|
TupleType *fromTuple,
|
|
TupleType *toTuple,
|
|
ConstraintLocatorBuilder locator,
|
|
ArrayRef<unsigned> sources) {
|
|
// If the input expression is a tuple expression, we can convert it in-place.
|
|
if (canPeepholeTupleConversion(expr, sources)) {
|
|
auto *tupleExpr = cast<TupleExpr>(expr);
|
|
|
|
for (unsigned i = 0, e = tupleExpr->getNumElements(); i != e; ++i) {
|
|
auto *fromElt = tupleExpr->getElement(i);
|
|
|
|
// Actually convert the source element.
|
|
auto toEltType = toTuple->getElementType(i);
|
|
|
|
auto *toElt
|
|
= coerceToType(fromElt, toEltType,
|
|
locator.withPathElement(
|
|
LocatorPathElt::TupleElement(i)));
|
|
if (!toElt)
|
|
return nullptr;
|
|
|
|
tupleExpr->setElement(i, toElt);
|
|
}
|
|
|
|
tupleExpr->setType(toTuple);
|
|
cs.cacheType(tupleExpr);
|
|
|
|
return tupleExpr;
|
|
}
|
|
|
|
// Build a list of OpaqueValueExprs that matches the structure
|
|
// of expr's type.
|
|
//
|
|
// Each OpaqueValueExpr's type is equal to the type of the
|
|
// corresponding element of fromTuple.
|
|
SmallVector<OpaqueValueExpr *, 4> destructured;
|
|
for (unsigned i = 0, e = sources.size(); i != e; ++i) {
|
|
auto fromEltType = fromTuple->getElementType(i);
|
|
auto *opaqueElt =
|
|
new (ctx) OpaqueValueExpr(expr->getSourceRange(), fromEltType);
|
|
cs.cacheType(opaqueElt);
|
|
destructured.push_back(opaqueElt);
|
|
}
|
|
|
|
// Convert each OpaqueValueExpr to the correct type.
|
|
SmallVector<Expr *, 4> converted;
|
|
SmallVector<Identifier, 4> labels;
|
|
SmallVector<TupleTypeElt, 4> convertedElts;
|
|
|
|
bool anythingShuffled = false;
|
|
for (unsigned i = 0, e = sources.size(); i != e; ++i) {
|
|
unsigned source = sources[i];
|
|
auto *fromElt = destructured[source];
|
|
|
|
// Actually convert the source element.
|
|
auto toEltType = toTuple->getElementType(i);
|
|
auto toLabel = toTuple->getElement(i).getName();
|
|
|
|
// If we're shuffling positions and labels, we have to warn about this
|
|
// conversion.
|
|
if (i != sources[i] &&
|
|
fromTuple->getElement(i).getName() != toLabel)
|
|
anythingShuffled = true;
|
|
|
|
auto *toElt
|
|
= coerceToType(fromElt, toEltType,
|
|
locator.withPathElement(
|
|
LocatorPathElt::TupleElement(source)));
|
|
if (!toElt)
|
|
return nullptr;
|
|
|
|
converted.push_back(toElt);
|
|
labels.push_back(toLabel);
|
|
convertedElts.emplace_back(toEltType, toLabel);
|
|
}
|
|
|
|
// Shuffling tuple elements is an anti-pattern worthy of a diagnostic. We
|
|
// will form the shuffle for now, but a future compiler should decline to
|
|
// do so and begin the process of removing them altogether.
|
|
if (anythingShuffled) {
|
|
ctx.Diags.diagnose(
|
|
expr->getLoc(), diag::warn_reordering_tuple_shuffle_deprecated);
|
|
}
|
|
|
|
// Create the result tuple, written in terms of the destructured
|
|
// OpaqueValueExprs.
|
|
auto *result = TupleExpr::createImplicit(ctx, converted, labels);
|
|
result->setType(TupleType::get(convertedElts, ctx));
|
|
cs.cacheType(result);
|
|
|
|
// Create the tuple conversion.
|
|
return cs.cacheType(
|
|
DestructureTupleExpr::create(ctx, destructured, expr, result, toTuple));
|
|
}
|
|
|
|
static Type getMetatypeSuperclass(Type t) {
|
|
if (auto *metaTy = t->getAs<MetatypeType>())
|
|
return MetatypeType::get(getMetatypeSuperclass(
|
|
metaTy->getInstanceType()));
|
|
|
|
if (auto *metaTy = t->getAs<ExistentialMetatypeType>())
|
|
return ExistentialMetatypeType::get(getMetatypeSuperclass(
|
|
metaTy->getInstanceType()));
|
|
|
|
return t->getSuperclass();
|
|
}
|
|
|
|
Expr *ExprRewriter::coerceSuperclass(Expr *expr, Type toType) {
|
|
auto fromType = cs.getType(expr);
|
|
|
|
auto fromInstanceType = fromType;
|
|
auto toInstanceType = toType;
|
|
|
|
while (fromInstanceType->is<AnyMetatypeType>() &&
|
|
toInstanceType->is<MetatypeType>()) {
|
|
fromInstanceType = fromInstanceType->getMetatypeInstanceType();
|
|
toInstanceType = toInstanceType->getMetatypeInstanceType();
|
|
}
|
|
|
|
if (fromInstanceType->is<ArchetypeType>()) {
|
|
// Coercion from archetype to its (concrete) superclass.
|
|
auto superclass = getMetatypeSuperclass(fromType);
|
|
|
|
expr =
|
|
cs.cacheType(
|
|
new (ctx) ArchetypeToSuperExpr(expr, superclass));
|
|
|
|
if (!superclass->isEqual(toType))
|
|
return coerceSuperclass(expr, toType);
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
if (fromInstanceType->isExistentialType()) {
|
|
// Coercion from superclass-constrained existential to its
|
|
// concrete superclass.
|
|
auto fromArchetype =
|
|
ExistentialArchetypeType::getAny(fromType->getCanonicalType());
|
|
|
|
auto *archetypeVal = cs.cacheType(new (ctx) OpaqueValueExpr(
|
|
expr->getSourceRange(), fromArchetype));
|
|
|
|
auto *result = coerceSuperclass(archetypeVal, toType);
|
|
|
|
return cs.cacheType(
|
|
new (ctx) OpenExistentialExpr(expr, archetypeVal, result,
|
|
toType));
|
|
}
|
|
|
|
// Coercion from subclass to superclass.
|
|
if (toType->is<MetatypeType>()) {
|
|
return cs.cacheType(
|
|
new (ctx) MetatypeConversionExpr(expr, toType));
|
|
}
|
|
|
|
return cs.cacheType(
|
|
new (ctx) DerivedToBaseExpr(expr, toType));
|
|
}
|
|
|
|
/// Given that the given expression is an implicit conversion added
|
|
/// to the target by coerceToType, find out how many OptionalEvaluationExprs
|
|
/// it includes and the target.
|
|
static unsigned getOptionalEvaluationDepth(Expr *expr, Expr *target) {
|
|
unsigned depth = 0;
|
|
while (true) {
|
|
// Look through sugar expressions.
|
|
expr = expr->getSemanticsProvidingExpr();
|
|
|
|
// If we find the target expression, we're done.
|
|
if (expr == target) return depth;
|
|
|
|
// If we see an optional evaluation, the depth goes up.
|
|
if (auto optEval = dyn_cast<OptionalEvaluationExpr>(expr)) {
|
|
++depth;
|
|
expr = optEval->getSubExpr();
|
|
|
|
// We have to handle any other expressions that can be introduced by
|
|
// coerceToType.
|
|
} else if (auto bind = dyn_cast<BindOptionalExpr>(expr)) {
|
|
expr = bind->getSubExpr();
|
|
} else if (auto force = dyn_cast<ForceValueExpr>(expr)) {
|
|
expr = force->getSubExpr();
|
|
} else if (auto open = dyn_cast<OpenExistentialExpr>(expr)) {
|
|
depth += getOptionalEvaluationDepth(open->getSubExpr(),
|
|
open->getOpaqueValue());
|
|
expr = open->getExistentialValue();
|
|
} else if (auto call = dyn_cast<CallExpr>(expr)) {
|
|
// CGFloat <-> Double conversions lower to constructor calls.
|
|
expr = call->getArgs()->getExpr(0);
|
|
|
|
// Otherwise, look through implicit conversions.
|
|
} else {
|
|
expr = cast<ImplicitConversionExpr>(expr)->getSubExpr();
|
|
}
|
|
}
|
|
}
|
|
|
|
Expr *ExprRewriter::coerceOptionalToOptional(Expr *expr, Type toType,
|
|
ConstraintLocatorBuilder locator) {
|
|
Type fromType = cs.getType(expr);
|
|
|
|
TypeChecker::requireOptionalIntrinsics(ctx, expr->getLoc());
|
|
|
|
SmallVector<Type, 4> fromOptionals;
|
|
(void)fromType->lookThroughAllOptionalTypes(fromOptionals);
|
|
|
|
SmallVector<Type, 4> toOptionals;
|
|
(void)toType->lookThroughAllOptionalTypes(toOptionals);
|
|
|
|
assert(!toOptionals.empty());
|
|
assert(!fromOptionals.empty());
|
|
|
|
// If we are adding optionals but the types are equivalent up to the common
|
|
// depth, peephole the optional-to-optional conversion into a series of nested
|
|
// injections.
|
|
auto toDepth = toOptionals.size();
|
|
auto fromDepth = fromOptionals.size();
|
|
if (toDepth > fromDepth &&
|
|
toOptionals[toOptionals.size() - fromDepth]->isEqual(fromType)) {
|
|
auto diff = toDepth - fromDepth;
|
|
while (diff--) {
|
|
Type type = toOptionals[diff];
|
|
expr = cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, type));
|
|
diagnoseOptionalInjection(cast<InjectIntoOptionalExpr>(expr), locator);
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
Type fromValueType = fromType->getOptionalObjectType();
|
|
Type toValueType = toType->getOptionalObjectType();
|
|
|
|
// The depth we use here will get patched after we apply the coercion.
|
|
auto bindOptional =
|
|
new (ctx) BindOptionalExpr(expr, expr->getSourceRange().End,
|
|
/*depth*/ 0, fromValueType);
|
|
|
|
expr = cs.cacheType(bindOptional);
|
|
expr->setImplicit(true);
|
|
expr = coerceToType(expr, toValueType, locator);
|
|
if (!expr) return nullptr;
|
|
|
|
unsigned depth = getOptionalEvaluationDepth(expr, bindOptional);
|
|
bindOptional->setDepth(depth);
|
|
|
|
expr = cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, toType));
|
|
|
|
expr = cs.cacheType(new (ctx) OptionalEvaluationExpr(expr, toType));
|
|
expr->setImplicit(true);
|
|
return expr;
|
|
}
|
|
|
|
/// Determine whether the given expression is a reference to an
|
|
/// unbound instance member of a type.
|
|
static bool isReferenceToMetatypeMember(ConstraintSystem &cs, Expr *expr) {
|
|
expr = expr->getSemanticsProvidingExpr();
|
|
if (auto dotIgnored = dyn_cast<DotSyntaxBaseIgnoredExpr>(expr))
|
|
return cs.getType(dotIgnored->getLHS())->is<AnyMetatypeType>();
|
|
if (auto dotSyntax = dyn_cast<DotSyntaxCallExpr>(expr))
|
|
return cs.getType(dotSyntax->getBase())->is<AnyMetatypeType>();
|
|
return false;
|
|
}
|
|
|
|
static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee,
|
|
ApplyExpr *apply) {
|
|
// If we do not have a callee, return false.
|
|
if (!callee) {
|
|
return false;
|
|
}
|
|
|
|
// Only calls to members of types can have curried 'self'.
|
|
auto calleeDecl = callee.getDecl();
|
|
if (!calleeDecl->getDeclContext()->isTypeContext()) {
|
|
return false;
|
|
}
|
|
|
|
// Would have `self`, if we're not applying it.
|
|
if (auto *call = dyn_cast<CallExpr>(apply)) {
|
|
if (!calleeDecl->isInstanceMember() ||
|
|
!isReferenceToMetatypeMember(cs, call->getDirectCallee())) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Operators have curried self.
|
|
if (isa<PrefixUnaryExpr>(apply) || isa<PostfixUnaryExpr>(apply) ||
|
|
isa<BinaryExpr>(apply)) {
|
|
return true;
|
|
}
|
|
|
|
// Otherwise, we have a normal application.
|
|
return false;
|
|
}
|
|
|
|
/// Apply the contextually Sendable flag to the given expression,
|
|
static void applyContextualClosureFlags(Expr *expr, unsigned paramIdx,
|
|
const ParameterListInfo ¶mInfo,
|
|
bool requiresDynamicIsolationChecking,
|
|
bool isMacroArg) {
|
|
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
|
|
closure->setAllowsImplicitSelfCapture(
|
|
paramInfo.isImplicitSelfCapture(paramIdx));
|
|
|
|
auto [inheritActorContext, modifier] =
|
|
paramInfo.inheritsActorContext(paramIdx);
|
|
closure->setInheritsActorContext(inheritActorContext, modifier);
|
|
|
|
closure->setIsPassedToSendingParameter(
|
|
paramInfo.isSendingParameter(paramIdx));
|
|
closure->setRequiresDynamicIsolationChecking(
|
|
requiresDynamicIsolationChecking);
|
|
closure->setIsMacroArgument(isMacroArg);
|
|
return;
|
|
}
|
|
|
|
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
|
|
applyContextualClosureFlags(captureList->getClosureBody(), paramIdx,
|
|
paramInfo, requiresDynamicIsolationChecking,
|
|
isMacroArg);
|
|
}
|
|
|
|
if (auto identity = dyn_cast<IdentityExpr>(expr)) {
|
|
applyContextualClosureFlags(identity->getSubExpr(), paramIdx, paramInfo,
|
|
requiresDynamicIsolationChecking, isMacroArg);
|
|
}
|
|
}
|
|
|
|
// For variadic generic declarations we need to compute a substituted
|
|
// version of bindings because all of the packs are exploaded in the
|
|
// substituted function type.
|
|
//
|
|
// \code
|
|
// func fn<each T>(_: repeat each T) {}
|
|
//
|
|
// fn("", 42)
|
|
// \endcode
|
|
//
|
|
// The type of `fn` in the call is `(String, Int) -> Void` but bindings
|
|
// have only one parameter at index `0` with two argument positions: 0, 1.
|
|
static bool shouldSubstituteParameterBindings(ConcreteDeclRef callee) {
|
|
auto subst = callee.getSubstitutions();
|
|
if (subst.empty())
|
|
return false;
|
|
|
|
auto sig = subst.getGenericSignature();
|
|
return llvm::any_of(
|
|
sig.getGenericParams(),
|
|
[&](const GenericTypeParamType *GP) { return GP->isParameterPack(); });
|
|
}
|
|
|
|
/// Compute parameter binding substitutions by exploding pack expansions
|
|
/// into multiple bindings (if they matched more than one argument) and
|
|
/// ignoring empty ones.
|
|
static void computeParameterBindingsSubstitutions(
|
|
ConcreteDeclRef callee, ArrayRef<AnyFunctionType::Param> params,
|
|
ArrayRef<ParamBinding> origBindings,
|
|
SmallVectorImpl<ParamBinding> &substitutedBindings) {
|
|
for (unsigned bindingIdx = 0, numBindings = origBindings.size();
|
|
bindingIdx != numBindings; ++bindingIdx) {
|
|
if (origBindings[bindingIdx].size() > 1) {
|
|
const auto ¶m = params[substitutedBindings.size()];
|
|
if (!param.isVariadic()) {
|
|
#ifndef NDEBUG
|
|
auto *PD = getParameterAt(callee.getDecl(), bindingIdx);
|
|
assert(PD && PD->getInterfaceType()->is<PackExpansionType>());
|
|
#endif
|
|
// Explode binding set to match substituted function parameters.
|
|
for (auto argIdx : origBindings[bindingIdx])
|
|
substitutedBindings.push_back({argIdx});
|
|
continue;
|
|
}
|
|
}
|
|
|
|
const auto &bindings = origBindings[bindingIdx];
|
|
if (bindings.size() == 0) {
|
|
auto *PD = getParameterAt(callee.getDecl(), bindingIdx);
|
|
// Skip pack expansions with no arguments because they are not
|
|
// present in the substituted function type.
|
|
if (PD->getInterfaceType()->is<PackExpansionType>())
|
|
continue;
|
|
}
|
|
|
|
substitutedBindings.push_back(bindings);
|
|
}
|
|
}
|
|
|
|
ArgumentList *ExprRewriter::coerceCallArguments(
|
|
ArgumentList *args, AnyFunctionType *funcType, ConcreteDeclRef callee,
|
|
ApplyExpr *apply, ConstraintLocatorBuilder locator,
|
|
ArrayRef<AppliedPropertyWrapper> appliedPropertyWrappers) {
|
|
assert(locator.endsWith<LocatorPathElt::ApplyArgument>());
|
|
|
|
auto &ctx = getConstraintSystem().getASTContext();
|
|
auto params = funcType->getParams();
|
|
unsigned appliedWrapperIndex = 0;
|
|
|
|
// Local function to produce a locator to refer to the given parameter.
|
|
auto getArgLocator =
|
|
[&](unsigned argIdx, unsigned paramIdx,
|
|
ParameterTypeFlags flags) -> ConstraintLocatorBuilder {
|
|
return locator.withPathElement(
|
|
LocatorPathElt::ApplyArgToParam(argIdx, paramIdx, flags));
|
|
};
|
|
|
|
// Determine whether this application has curried self.
|
|
bool skipCurriedSelf = apply ? hasCurriedSelf(cs, callee, apply) : true;
|
|
// Determine the parameter bindings.
|
|
ParameterListInfo paramInfo(params, callee.getDecl(), skipCurriedSelf);
|
|
|
|
// If this application is an init(wrappedValue:) call that needs an injected
|
|
// wrapped value placeholder, the first non-defaulted argument must be
|
|
// wrapped in an OpaqueValueExpr.
|
|
bool shouldInjectWrappedValuePlaceholder =
|
|
target && target->shouldInjectWrappedValuePlaceholder(apply);
|
|
|
|
auto injectWrappedValuePlaceholder =
|
|
[&](Expr *arg, bool isAutoClosure = false) -> Expr * {
|
|
auto *placeholder = PropertyWrapperValuePlaceholderExpr::create(
|
|
ctx, arg->getSourceRange(), cs.getType(arg),
|
|
target->propertyWrapperHasInitialWrappedValue() ? arg : nullptr,
|
|
isAutoClosure);
|
|
cs.cacheType(placeholder);
|
|
cs.cacheType(placeholder->getOpaqueValuePlaceholder());
|
|
shouldInjectWrappedValuePlaceholder = false;
|
|
return placeholder;
|
|
};
|
|
|
|
bool closuresRequireDynamicIsolationChecking = [&]() {
|
|
auto *decl = callee.getDecl();
|
|
// If this is something like `{ @MainActor in ... }()`, let's consider
|
|
// callee as concurrency checked.
|
|
if (!decl)
|
|
return false;
|
|
|
|
if (auto declaredIn = decl->findImport(dc))
|
|
return !declaredIn->module.importedModule->isConcurrencyChecked();
|
|
|
|
// Both the caller and the allee are in the same module.
|
|
if (dc->getParentModule() == decl->getModuleContext()) {
|
|
return !dc->getASTContext().isSwiftVersionAtLeast(6);
|
|
}
|
|
|
|
// If we cannot figure out where the callee came from, let's conservatively
|
|
// assume that closure arguments require dynamic isolation checks.
|
|
return true;
|
|
}();
|
|
|
|
auto applyFlagsToArgument = [¶mInfo,
|
|
&closuresRequireDynamicIsolationChecking,
|
|
&locator](unsigned paramIdx, Expr *argument) {
|
|
if (!isClosureLiteralExpr(argument))
|
|
return;
|
|
|
|
bool isMacroArg = isExpr<MacroExpansionExpr>(locator.getAnchor());
|
|
|
|
applyContextualClosureFlags(argument, paramIdx, paramInfo,
|
|
closuresRequireDynamicIsolationChecking,
|
|
isMacroArg);
|
|
};
|
|
|
|
// Quickly test if any further fix-ups for the argument types are necessary.
|
|
auto matches = args->matches(params, [&](Expr *E) { return cs.getType(E); });
|
|
if (matches && !shouldInjectWrappedValuePlaceholder) {
|
|
for (unsigned paramIdx : indices(params)) {
|
|
applyFlagsToArgument(paramIdx, args->getExpr(paramIdx));
|
|
}
|
|
return args;
|
|
}
|
|
|
|
// Determine the parameter bindings that were applied.
|
|
auto *locatorPtr = cs.getConstraintLocator(locator);
|
|
assert(solution.argumentMatchingChoices.count(locatorPtr) == 1);
|
|
auto parameterBindings = solution.argumentMatchingChoices.find(locatorPtr)
|
|
->second.parameterBindings;
|
|
bool shouldSubstituteBindings = shouldSubstituteParameterBindings(callee);
|
|
|
|
SmallVector<ParamBinding, 4> substitutedBindings;
|
|
if (shouldSubstituteBindings) {
|
|
computeParameterBindingsSubstitutions(callee, params, parameterBindings,
|
|
substitutedBindings);
|
|
} else {
|
|
substitutedBindings = parameterBindings;
|
|
}
|
|
|
|
SmallVector<Argument, 4> newArgs;
|
|
for (unsigned paramIdx = 0, numParams = substitutedBindings.size();
|
|
paramIdx != numParams; ++paramIdx) {
|
|
// Extract the parameter.
|
|
const auto ¶m = params[paramIdx];
|
|
auto paramLabel = param.getLabel();
|
|
|
|
// Handle variadic parameters.
|
|
if (param.isVariadic()) {
|
|
assert(!param.isInOut());
|
|
|
|
SmallVector<Expr *, 4> variadicArgs;
|
|
|
|
// The first argument of this vararg parameter may have had a label;
|
|
// save its location.
|
|
auto &varargIndices = substitutedBindings[paramIdx];
|
|
SourceLoc labelLoc;
|
|
if (!varargIndices.empty())
|
|
labelLoc = args->getLabelLoc(varargIndices[0]);
|
|
|
|
// Convert the arguments.
|
|
for (auto argIdx : varargIndices) {
|
|
auto *arg = args->getExpr(argIdx);
|
|
auto argType = cs.getType(arg);
|
|
|
|
// If the argument type exactly matches, this just works.
|
|
if (argType->isEqual(param.getPlainType())) {
|
|
variadicArgs.push_back(arg);
|
|
continue;
|
|
}
|
|
|
|
// Convert the argument.
|
|
auto convertedArg = coerceToType(
|
|
arg, param.getPlainType(),
|
|
getArgLocator(argIdx, paramIdx, param.getParameterFlags()));
|
|
if (!convertedArg)
|
|
return nullptr;
|
|
|
|
// Add the converted argument.
|
|
variadicArgs.push_back(convertedArg);
|
|
}
|
|
|
|
SourceLoc start, end;
|
|
if (!variadicArgs.empty()) {
|
|
start = variadicArgs.front()->getStartLoc();
|
|
end = variadicArgs.back()->getEndLoc();
|
|
}
|
|
|
|
// Collect them into an ArrayExpr.
|
|
auto *arrayExpr = ArrayExpr::create(ctx, start, variadicArgs, {}, end,
|
|
param.getParameterType());
|
|
arrayExpr->setImplicit();
|
|
cs.cacheType(arrayExpr);
|
|
|
|
// Wrap the ArrayExpr in a VarargExpansionExpr.
|
|
auto *varargExpansionExpr =
|
|
VarargExpansionExpr::createArrayExpansion(ctx, arrayExpr);
|
|
cs.cacheType(varargExpansionExpr);
|
|
|
|
newArgs.push_back(Argument(labelLoc, paramLabel, varargExpansionExpr));
|
|
continue;
|
|
}
|
|
|
|
// Handle default arguments.
|
|
if (substitutedBindings[paramIdx].empty()) {
|
|
auto paramIdxForDefault = paramIdx;
|
|
// If bindings were substituted we need to find "original"
|
|
// (or contextless) parameter index for the default argument.
|
|
if (shouldSubstituteBindings) {
|
|
auto *paramList = callee.getDecl()->getParameterList();
|
|
ASSERT(paramList);
|
|
paramIdxForDefault =
|
|
paramList->getOrigParamIndex(callee.getSubstitutions(), paramIdx);
|
|
}
|
|
|
|
auto owner = getDefaultArgOwner(callee, paramIdx);
|
|
auto paramTy = param.getParameterType();
|
|
auto *defArg = new (ctx) DefaultArgumentExpr(
|
|
owner, paramIdxForDefault, args->getStartLoc(), paramTy, dc);
|
|
|
|
cs.cacheType(defArg);
|
|
newArgs.emplace_back(SourceLoc(), param.getLabel(), defArg);
|
|
continue;
|
|
}
|
|
|
|
// Otherwise, we have a plain old ordinary argument.
|
|
|
|
// Extract the argument used to initialize this parameter.
|
|
assert(substitutedBindings[paramIdx].size() == 1);
|
|
unsigned argIdx = substitutedBindings[paramIdx].front();
|
|
auto arg = args->get(argIdx);
|
|
auto *argExpr = arg.getExpr();
|
|
auto argType = cs.getType(argExpr);
|
|
|
|
// Update the argument label to match the parameter. This may be necessary
|
|
// for things like trailing closures and args to property wrapper params.
|
|
arg.setLabel(param.getLabel());
|
|
|
|
// Determine whether the argument should be marked as having
|
|
// implicit self capture, inheriting actor context, is passed to a
|
|
// `sending` parameter etc.
|
|
applyFlagsToArgument(paramIdx, argExpr);
|
|
|
|
auto canShortcutConversion = [&](Type argType, Type paramType) {
|
|
if (shouldInjectWrappedValuePlaceholder ||
|
|
paramInfo.hasExternalPropertyWrapper(paramIdx))
|
|
return false;
|
|
|
|
return argType->isEqual(paramType);
|
|
};
|
|
|
|
auto paramType = param.getOldType();
|
|
|
|
if (canShortcutConversion(argType, paramType)) {
|
|
newArgs.push_back(arg);
|
|
continue;
|
|
}
|
|
|
|
Expr *convertedArg = nullptr;
|
|
auto argRequiresAutoClosureExpr = [&](const AnyFunctionType::Param ¶m,
|
|
Type argType) {
|
|
if (!param.isAutoClosure())
|
|
return false;
|
|
|
|
// Since it was allowed to pass function types to @autoclosure
|
|
// parameters in Swift versions < 5, it has to be handled as
|
|
// a regular function conversion by `coerceToType`.
|
|
if (isAutoClosureArgument(argExpr)) {
|
|
// In Swift >= 5 mode we only allow `@autoclosure` arguments
|
|
// to be used by value if parameter would return a function
|
|
// type (it just needs to get wrapped into autoclosure expr),
|
|
// otherwise argument must always form a call.
|
|
return ctx.isSwiftVersionAtLeast(5);
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
if (paramInfo.hasExternalPropertyWrapper(paramIdx)) {
|
|
auto *paramDecl = getParameterAt(callee, paramIdx);
|
|
assert(paramDecl);
|
|
|
|
ASSERT(appliedWrapperIndex < appliedPropertyWrappers.size());
|
|
auto appliedWrapper = appliedPropertyWrappers[appliedWrapperIndex++];
|
|
auto wrapperType = solution.simplifyType(appliedWrapper.wrapperType);
|
|
auto initKind = appliedWrapper.initKind;
|
|
|
|
AppliedPropertyWrapperExpr::ValueKind valueKind;
|
|
PropertyWrapperValuePlaceholderExpr *generatorArg;
|
|
auto initInfo = paramDecl->getPropertyWrapperInitializerInfo();
|
|
if (initKind == PropertyWrapperInitKind::ProjectedValue) {
|
|
valueKind = AppliedPropertyWrapperExpr::ValueKind::ProjectedValue;
|
|
generatorArg = initInfo.getProjectedValuePlaceholder();
|
|
} else {
|
|
valueKind = AppliedPropertyWrapperExpr::ValueKind::WrappedValue;
|
|
generatorArg = initInfo.getWrappedValuePlaceholder();
|
|
}
|
|
|
|
// Coerce the property wrapper argument type to the input type of
|
|
// the property wrapper generator function. The wrapper generator
|
|
// has the same generic signature as the enclosing function, so we
|
|
// can use substitutions from the callee.
|
|
Type generatorInputType =
|
|
generatorArg->getType().subst(callee.getSubstitutions());
|
|
auto argLoc = getArgLocator(argIdx, paramIdx, param.getParameterFlags());
|
|
|
|
if (generatorArg->isAutoClosure()) {
|
|
auto *closureType = generatorInputType->castTo<FunctionType>();
|
|
argExpr = coerceToType(
|
|
argExpr, closureType->getResult(),
|
|
argLoc.withPathElement(ConstraintLocator::AutoclosureResult));
|
|
argExpr = cs.buildAutoClosureExpr(argExpr, closureType, dc);
|
|
}
|
|
|
|
argExpr = coerceToType(argExpr, generatorInputType, argLoc);
|
|
|
|
// Wrap the argument in an applied property wrapper expr, which will
|
|
// later turn into a call to the property wrapper generator function.
|
|
argExpr = AppliedPropertyWrapperExpr::create(ctx, callee, paramDecl,
|
|
argExpr->getStartLoc(),
|
|
wrapperType, argExpr,
|
|
valueKind);
|
|
cs.cacheExprTypes(argExpr);
|
|
}
|
|
|
|
auto argLoc = getArgLocator(argIdx, paramIdx, param.getParameterFlags());
|
|
|
|
// If the argument is an existential type that has been opened, perform
|
|
// the open operation.
|
|
if (argType->getWithoutSpecifierType()->isAnyExistentialType() &&
|
|
paramType->hasOpenedExistential()) {
|
|
// FIXME: Look for an opened existential and use it. We need to
|
|
// know how far out we need to go to close the existentials. Huh.
|
|
auto knownOpened = solution.OpenedExistentialTypes.find(
|
|
cs.getConstraintLocator(argLoc));
|
|
if (knownOpened != solution.OpenedExistentialTypes.end()) {
|
|
argExpr = openExistentialReference(
|
|
argExpr, knownOpened->second, callee.getDecl(), apply->getLoc());
|
|
argType = cs.getType(argExpr);
|
|
}
|
|
}
|
|
|
|
if (argRequiresAutoClosureExpr(param, argType)) {
|
|
assert(!param.isInOut());
|
|
|
|
// If parameter is an autoclosure, we need to make sure that:
|
|
// - argument type is coerced to parameter result type
|
|
// - implicit autoclosure is created to wrap argument expression
|
|
// - new types are propagated to constraint system
|
|
auto *closureType = param.getPlainType()->castTo<FunctionType>();
|
|
|
|
argExpr = coerceToType(
|
|
argExpr, closureType->getResult(),
|
|
argLoc.withPathElement(ConstraintLocator::AutoclosureResult));
|
|
|
|
if (shouldInjectWrappedValuePlaceholder) {
|
|
// If init(wrappedValue:) takes an autoclosure, then we want
|
|
// the effect of autoclosure forwarding of the placeholder
|
|
// autoclosure. The only way to do this is to call the placeholder
|
|
// autoclosure when passing it to the init.
|
|
bool isDefaultWrappedValue =
|
|
target->propertyWrapperHasInitialWrappedValue();
|
|
auto *placeholder = injectWrappedValuePlaceholder(
|
|
cs.buildAutoClosureExpr(argExpr, closureType, dc,
|
|
isDefaultWrappedValue),
|
|
/*isAutoClosure=*/true);
|
|
argExpr = CallExpr::createImplicitEmpty(ctx, placeholder);
|
|
argExpr->setType(closureType->getResult());
|
|
cs.cacheType(argExpr);
|
|
}
|
|
|
|
convertedArg = cs.buildAutoClosureExpr(argExpr, closureType, dc);
|
|
} else {
|
|
convertedArg = coerceToType(argExpr, paramType, argLoc);
|
|
}
|
|
|
|
// Perform the wrapped value placeholder injection
|
|
if (shouldInjectWrappedValuePlaceholder)
|
|
convertedArg = injectWrappedValuePlaceholder(convertedArg);
|
|
|
|
if (!convertedArg)
|
|
return nullptr;
|
|
|
|
// Write back the rewritten argument to the original argument list. This
|
|
// ensures it has the same semantic argument information as the rewritten
|
|
// argument list, which may be required by IDE logic.
|
|
args->setExpr(argIdx, convertedArg);
|
|
|
|
arg.setExpr(convertedArg);
|
|
newArgs.push_back(arg);
|
|
}
|
|
|
|
ASSERT(appliedWrapperIndex == appliedPropertyWrappers.size());
|
|
return ArgumentList::createTypeChecked(ctx, args, newArgs);
|
|
}
|
|
|
|
/// Looks through any non-semantic expressions and a capture list
|
|
/// to find out whether the given expression is an explicit closure.
|
|
static ClosureExpr *isExplicitClosureExpr(Expr *expr) {
|
|
if (auto IE = dyn_cast<IdentityExpr>(expr))
|
|
return isExplicitClosureExpr(IE->getSubExpr());
|
|
|
|
if (auto CLE = dyn_cast<CaptureListExpr>(expr))
|
|
return isExplicitClosureExpr(CLE->getClosureBody());
|
|
|
|
return dyn_cast<ClosureExpr>(expr);
|
|
}
|
|
|
|
/// Whether the given expression is a closure that should inherit
|
|
/// the actor context from where it was formed.
|
|
static bool closureInheritsActorContext(Expr *expr) {
|
|
if (auto *CE = isExplicitClosureExpr(expr))
|
|
return CE->inheritsActorContext();
|
|
return false;
|
|
}
|
|
|
|
/// Determine whether the given expression is a closure that
|
|
/// is explicitly marked as `@concurrent`.
|
|
static bool isClosureMarkedAsConcurrent(Expr *expr) {
|
|
if (auto *CE = isExplicitClosureExpr(expr))
|
|
return CE->getAttrs().hasAttribute<ConcurrentAttr>();
|
|
return false;
|
|
}
|
|
|
|
/// If the expression is an explicit closure expression (potentially wrapped in
|
|
/// IdentityExprs), change the type of the closure and identities to the
|
|
/// specified type and return true. Otherwise, return false with no effect.
|
|
static bool applyTypeToClosureExpr(ConstraintSystem &cs,
|
|
Expr *expr, Type toType) {
|
|
// Look through identity expressions, like parens.
|
|
if (auto IE = dyn_cast<IdentityExpr>(expr)) {
|
|
if (!applyTypeToClosureExpr(cs, IE->getSubExpr(), toType))
|
|
return false;
|
|
|
|
cs.setType(IE, cs.getType(IE->getSubExpr()));
|
|
return true;
|
|
}
|
|
|
|
// Look through capture lists.
|
|
if (auto CLE = dyn_cast<CaptureListExpr>(expr)) {
|
|
if (!applyTypeToClosureExpr(cs, CLE->getClosureBody(), toType)) return false;
|
|
cs.setType(CLE, toType);
|
|
return true;
|
|
}
|
|
|
|
// If we found an explicit ClosureExpr, update its type.
|
|
if (auto CE = dyn_cast<ClosureExpr>(expr)) {
|
|
cs.setType(CE, toType);
|
|
return true;
|
|
}
|
|
|
|
// Otherwise fail.
|
|
return false;
|
|
}
|
|
|
|
// Look through sugar and DotSyntaxBaseIgnoredExprs.
|
|
static Expr *
|
|
getSemanticExprForDeclOrMemberRef(Expr *expr) {
|
|
auto semanticExpr = expr->getSemanticsProvidingExpr();
|
|
while (auto ignoredBase = dyn_cast<DotSyntaxBaseIgnoredExpr>(semanticExpr)){
|
|
semanticExpr = ignoredBase->getRHS()->getSemanticsProvidingExpr();
|
|
}
|
|
return semanticExpr;
|
|
}
|
|
|
|
static void
|
|
maybeDiagnoseUnsupportedDifferentiableConversion(ConstraintSystem &cs,
|
|
Expr *expr,
|
|
AnyFunctionType *toType) {
|
|
ASTContext &ctx = cs.getASTContext();
|
|
Type fromType = cs.getType(expr);
|
|
auto fromFnType = fromType->getAs<AnyFunctionType>();
|
|
// Conversion between two different differentiable function types is not
|
|
// yet supported.
|
|
if (fromFnType->isDifferentiable() && toType->isDifferentiable() &&
|
|
fromFnType->getDifferentiabilityKind() !=
|
|
toType->getDifferentiabilityKind()) {
|
|
ctx.Diags.diagnose(expr->getLoc(),
|
|
diag::invalid_differentiable_function_conversion_expr);
|
|
return;
|
|
}
|
|
// Conversion from a non-`@differentiable` function to a `@differentiable` is
|
|
// only allowed from a closure expression or a declaration/member reference.
|
|
if (!fromFnType->isDifferentiable() && toType->isDifferentiable()) {
|
|
std::function<void(Expr *)> maybeDiagnoseFunctionRef;
|
|
maybeDiagnoseFunctionRef = [&](Expr *semanticExpr) {
|
|
if (auto *capture = dyn_cast<CaptureListExpr>(semanticExpr))
|
|
semanticExpr = capture->getClosureBody();
|
|
if (isa<ClosureExpr>(semanticExpr)) return;
|
|
if (auto *declRef = dyn_cast<DeclRefExpr>(semanticExpr)) {
|
|
if (isa<AbstractFunctionDecl>(declRef->getDecl())) return;
|
|
// If the referenced decl is a function parameter, the user may want
|
|
// to change the declaration to be a '@differentiable' closure. Emit a
|
|
// note with a fix-it.
|
|
if (auto *paramDecl = dyn_cast<ParamDecl>(declRef->getDecl())) {
|
|
ctx.Diags.diagnose(
|
|
expr->getLoc(),
|
|
diag::invalid_differentiable_function_conversion_expr);
|
|
if (paramDecl->getInterfaceType()->is<AnyFunctionType>()) {
|
|
auto *typeRepr = paramDecl->getTypeRepr();
|
|
while (auto *attributed = dyn_cast<AttributedTypeRepr>(typeRepr))
|
|
typeRepr = attributed->getTypeRepr();
|
|
std::string attributeString = "@differentiable";
|
|
auto *funcTypeRepr = cast<FunctionTypeRepr>(typeRepr);
|
|
auto paramListLoc = funcTypeRepr->getArgsTypeRepr()->getStartLoc();
|
|
ctx.Diags.diagnose(paramDecl->getLoc(),
|
|
diag::invalid_differentiable_function_conversion_parameter,
|
|
attributeString)
|
|
.highlight(paramDecl->getTypeRepr()->getSourceRange())
|
|
.fixItInsert(paramListLoc, attributeString + " ");
|
|
}
|
|
return;
|
|
}
|
|
} else if (auto *memberRef = dyn_cast<MemberRefExpr>(semanticExpr)) {
|
|
if (isa<FuncDecl>(memberRef->getMember().getDecl())) return;
|
|
} else if (auto *dotSyntaxCall =
|
|
dyn_cast<DotSyntaxCallExpr>(semanticExpr)) {
|
|
// Recurse on the function expression.
|
|
auto *fnExpr = dotSyntaxCall->getFn()->getSemanticsProvidingExpr();
|
|
maybeDiagnoseFunctionRef(fnExpr);
|
|
return;
|
|
} else if (auto *autoclosureExpr = dyn_cast<AutoClosureExpr>(semanticExpr)) {
|
|
// Peer through curry thunks.
|
|
if (auto *unwrappedFnExpr = autoclosureExpr->getUnwrappedCurryThunkExpr()) {
|
|
maybeDiagnoseFunctionRef(unwrappedFnExpr);
|
|
return;
|
|
}
|
|
} else if (auto conv = dyn_cast<FunctionConversionExpr>(semanticExpr)) {
|
|
// Look through a function conversion that only adds or removes
|
|
// `@Sendable`.
|
|
auto ty1 = conv->getType()->castTo<AnyFunctionType>();
|
|
auto ty2 = conv->getSubExpr()->getType()->castTo<AnyFunctionType>();
|
|
|
|
if (ty1->withExtInfo(ty1->getExtInfo().withSendable(false))
|
|
->isEqual(ty2->withExtInfo(ty2->getExtInfo().withSendable(false)))) {
|
|
maybeDiagnoseFunctionRef(conv->getSubExpr()->getSemanticsProvidingExpr());
|
|
return;
|
|
}
|
|
}
|
|
|
|
ctx.Diags.diagnose(expr->getLoc(),
|
|
diag::invalid_differentiable_function_conversion_expr);
|
|
};
|
|
maybeDiagnoseFunctionRef(getSemanticExprForDeclOrMemberRef(expr));
|
|
}
|
|
}
|
|
|
|
static void
|
|
maybeDiagnoseUnsupportedFunctionConversion(ConstraintSystem &cs, Expr *expr,
|
|
AnyFunctionType *toType) {
|
|
auto &de = cs.getASTContext().Diags;
|
|
Type fromType = cs.getType(expr);
|
|
auto fromFnType = fromType->getAs<AnyFunctionType>();
|
|
|
|
// Conversions to C function pointer type are limited. Since a C function
|
|
// pointer captures no context, we can only do the necessary thunking or
|
|
// codegen if the original function is a direct reference to a global function
|
|
// or context-free closure or local function.
|
|
if (toType->getRepresentation()
|
|
== AnyFunctionType::Representation::CFunctionPointer) {
|
|
// Can convert from an ABI-compatible C function pointer.
|
|
if (fromFnType
|
|
&& fromFnType->getRepresentation()
|
|
== AnyFunctionType::Representation::CFunctionPointer)
|
|
return;
|
|
|
|
// Can convert a decl ref to a global or local function that doesn't
|
|
// capture context. Look through ignored bases too.
|
|
// TODO: Look through static method applications to the type.
|
|
auto semanticExpr = getSemanticExprForDeclOrMemberRef(expr);
|
|
auto maybeDiagnoseFunctionRef = [&](FuncDecl *fn) {
|
|
// TODO: We could allow static (or class final) functions too by
|
|
// "capturing" the metatype in a thunk.
|
|
if (fn->getDeclContext()->isTypeContext()) {
|
|
de.diagnose(expr->getLoc(), diag::c_function_pointer_from_method);
|
|
} else if (fn->getGenericParams()) {
|
|
de.diagnose(expr->getLoc(),
|
|
diag::c_function_pointer_from_generic_function);
|
|
}
|
|
};
|
|
|
|
// Look through a function conversion that only adds or removes
|
|
// `@Sendable`.
|
|
if (auto conv = dyn_cast<FunctionConversionExpr>(semanticExpr)) {
|
|
auto ty1 = conv->getType()->castTo<AnyFunctionType>();
|
|
auto ty2 = conv->getSubExpr()->getType()->castTo<AnyFunctionType>();
|
|
if (ty1->withExtInfo(ty1->getExtInfo().withSendable(false))
|
|
->isEqual(ty2->withExtInfo(ty2->getExtInfo().withSendable(false)))){
|
|
semanticExpr = conv->getSubExpr()->getSemanticsProvidingExpr();
|
|
}
|
|
}
|
|
|
|
if (auto declRef = dyn_cast<DeclRefExpr>(semanticExpr)) {
|
|
if (auto fn = dyn_cast<FuncDecl>(declRef->getDecl())) {
|
|
return maybeDiagnoseFunctionRef(fn);
|
|
}
|
|
}
|
|
|
|
if (auto memberRef = dyn_cast<MemberRefExpr>(semanticExpr)) {
|
|
if (auto fn = dyn_cast<FuncDecl>(memberRef->getMember().getDecl())) {
|
|
return maybeDiagnoseFunctionRef(fn);
|
|
}
|
|
}
|
|
|
|
// Unwrap closures with explicit capture lists.
|
|
if (auto capture = dyn_cast<CaptureListExpr>(semanticExpr))
|
|
semanticExpr = capture->getClosureBody();
|
|
|
|
// Can convert a literal closure that doesn't capture context.
|
|
if (isa<ClosureExpr>(semanticExpr))
|
|
return;
|
|
|
|
// Diagnose cases like:
|
|
// func f() { print(w) }; func g(_ : @convention(c) () -> ()) {}
|
|
// let k = f; g(k) // error
|
|
// func m() { let x = 0; g({ print(x) }) } // error
|
|
// (See also: [NOTE: diagnose-swift-to-c-convention-change])
|
|
de.diagnose(expr->getLoc(),
|
|
diag::invalid_c_function_pointer_conversion_expr);
|
|
}
|
|
}
|
|
|
|
/// Build the conversion of an element in a collection upcast.
|
|
static Expr *buildElementConversion(ExprRewriter &rewriter,
|
|
SourceRange srcRange, Type srcType,
|
|
Type destType, bool bridged,
|
|
ConstraintLocatorBuilder locator,
|
|
Expr *element) {
|
|
if (bridged && TypeChecker::typeCheckCheckedCast(
|
|
srcType, destType, CheckedCastContextKind::None,
|
|
rewriter.dc) != CheckedCastKind::Coercion) {
|
|
if (auto conversion =
|
|
rewriter.buildObjCBridgeExpr(element, destType, locator))
|
|
return conversion;
|
|
}
|
|
|
|
return rewriter.coerceToType(element, destType, locator);
|
|
}
|
|
|
|
static CollectionUpcastConversionExpr::ConversionPair
|
|
buildOpaqueElementConversion(ExprRewriter &rewriter, SourceRange srcRange,
|
|
Type srcType, Type destType,
|
|
bool bridged, ConstraintLocatorBuilder locator,
|
|
unsigned typeArgIndex) {
|
|
// Build the conversion.
|
|
auto &cs = rewriter.getConstraintSystem();
|
|
ASTContext &ctx = cs.getASTContext();
|
|
auto opaque =
|
|
rewriter.cs.cacheType(new (ctx) OpaqueValueExpr(srcRange, srcType));
|
|
|
|
Expr *conversion = buildElementConversion(
|
|
rewriter, srcRange, srcType, destType, bridged,
|
|
locator.withPathElement(LocatorPathElt::GenericArgument(typeArgIndex)),
|
|
opaque);
|
|
|
|
return { opaque, conversion };
|
|
}
|
|
|
|
void ExprRewriter::peepholeArrayUpcast(ArrayExpr *expr, Type toType,
|
|
bool bridged, Type elementType,
|
|
ConstraintLocatorBuilder locator) {
|
|
// Update the type of the array literal.
|
|
cs.setType(expr, toType);
|
|
// FIXME: finish{Array,Dictionary}Expr invoke cacheExprTypes after forming
|
|
// the semantic expression for the dictionary literal, which will undo the
|
|
// type we set here if this dictionary literal is nested unless we update
|
|
// the expr type as well.
|
|
expr->setType(toType);
|
|
|
|
// Convert the elements.
|
|
ConstraintLocatorBuilder innerLocator =
|
|
locator.withPathElement(LocatorPathElt::GenericArgument(0));
|
|
for (auto &element : expr->getElements()) {
|
|
if (auto newElement = buildElementConversion(*this, expr->getLoc(),
|
|
cs.getType(element),
|
|
elementType,
|
|
bridged, innerLocator,
|
|
element)) {
|
|
element = newElement;
|
|
}
|
|
}
|
|
|
|
(void)finishArrayExpr(expr);
|
|
}
|
|
|
|
void ExprRewriter::peepholeDictionaryUpcast(DictionaryExpr *expr,
|
|
Type toType, bool bridged,
|
|
Type keyType, Type valueType,
|
|
ConstraintLocatorBuilder locator) {
|
|
// Update the type of the dictionary literal.
|
|
cs.setType(expr, toType);
|
|
// FIXME: finish{Array,Dictionary}Expr invoke cacheExprTypes after forming
|
|
// the semantic expression for the dictionary literal, which will undo the
|
|
// type we set here if this dictionary literal is nested unless we update
|
|
// the expr type as well.
|
|
expr->setType(toType);
|
|
|
|
ConstraintLocatorBuilder valueLocator =
|
|
locator.withPathElement(LocatorPathElt::GenericArgument(1));
|
|
|
|
// Convert the elements.
|
|
TupleTypeElt tupleTypeElts[2] = { keyType, valueType };
|
|
auto tupleType = TupleType::get(tupleTypeElts, ctx);
|
|
for (auto element : expr->getElements()) {
|
|
if (auto tuple = dyn_cast<TupleExpr>(element)) {
|
|
auto key = tuple->getElement(0);
|
|
if (auto newKey = buildElementConversion(*this, expr->getLoc(),
|
|
cs.getType(key), keyType,
|
|
bridged, valueLocator, key))
|
|
tuple->setElement(0, newKey);
|
|
|
|
auto value = tuple->getElement(1);
|
|
if (auto newValue = buildElementConversion(*this, expr->getLoc(),
|
|
cs.getType(value), valueType,
|
|
bridged, valueLocator,
|
|
value)) {
|
|
tuple->setElement(1, newValue);
|
|
}
|
|
|
|
cs.setType(tuple, tupleType);
|
|
// FIXME: finish{Array,Dictionary}Expr invoke cacheExprTypes after forming
|
|
// the semantic expression for the dictionary literal, which will undo the
|
|
// type we set here if this dictionary literal is nested unless we update
|
|
// the expr type as well.
|
|
tuple->setType(tupleType);
|
|
}
|
|
}
|
|
|
|
(void)finishDictionaryExpr(expr);
|
|
}
|
|
|
|
bool ExprRewriter::peepholeCollectionUpcast(Expr *expr, Type toType,
|
|
bool bridged,
|
|
ConstraintLocatorBuilder locator) {
|
|
// Recur into parenthesized expressions.
|
|
if (auto paren = dyn_cast<ParenExpr>(expr)) {
|
|
// If we can't peephole the subexpression, we're done.
|
|
if (!peepholeCollectionUpcast(paren->getSubExpr(), toType, bridged,
|
|
locator))
|
|
return false;
|
|
|
|
// Update the type of this expression.
|
|
auto parenTy = cs.getType(paren->getSubExpr());
|
|
cs.setType(paren, parenTy);
|
|
// FIXME: finish{Array,Dictionary}Expr invoke cacheExprTypes after forming
|
|
// the semantic expression for the dictionary literal, which will undo the
|
|
// type we set here if this dictionary literal is nested unless we update
|
|
// the expr type as well.
|
|
paren->setType(parenTy);
|
|
return true;
|
|
}
|
|
|
|
// Array literals.
|
|
if (auto arrayLiteral = dyn_cast<ArrayExpr>(expr)) {
|
|
if (auto elementType = toType->getArrayElementType()) {
|
|
peepholeArrayUpcast(arrayLiteral, toType, bridged, elementType, locator);
|
|
return true;
|
|
}
|
|
|
|
if (std::optional<Type> elementType = ConstraintSystem::isSetType(toType)) {
|
|
peepholeArrayUpcast(arrayLiteral, toType, bridged, *elementType, locator);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Dictionary literals.
|
|
if (auto dictLiteral = dyn_cast<DictionaryExpr>(expr)) {
|
|
if (auto elementType = ConstraintSystem::isDictionaryType(toType)) {
|
|
peepholeDictionaryUpcast(dictLiteral, toType, bridged,
|
|
elementType->first, elementType->second,
|
|
locator);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Expr *ExprRewriter::buildCollectionUpcastExpr(
|
|
Expr *expr, Type toType,
|
|
bool bridged,
|
|
ConstraintLocatorBuilder locator) {
|
|
if (peepholeCollectionUpcast(expr, toType, bridged, locator))
|
|
return expr;
|
|
|
|
// Build the first value conversion.
|
|
auto fromArgs = cs.getType(expr)->castTo<BoundGenericType>()->getGenericArgs();
|
|
auto toArgs = toType->castTo<BoundGenericType>()->getGenericArgs();
|
|
auto conv =
|
|
buildOpaqueElementConversion(*this, expr->getLoc(),
|
|
fromArgs[0], toArgs[0],
|
|
bridged, locator, 0);
|
|
|
|
// For single-parameter collections, form the upcast.
|
|
if (toType->isArray() || ConstraintSystem::isSetType(toType)) {
|
|
return cs.cacheType(
|
|
new (ctx) CollectionUpcastConversionExpr(expr, toType, {}, conv));
|
|
}
|
|
|
|
assert(ConstraintSystem::isDictionaryType(toType) &&
|
|
"Unhandled collection upcast");
|
|
|
|
// Build the second value conversion.
|
|
auto conv2 =
|
|
buildOpaqueElementConversion(*this, expr->getLoc(),
|
|
fromArgs[1], toArgs[1],
|
|
bridged, locator, 1);
|
|
|
|
return cs.cacheType(
|
|
new (ctx) CollectionUpcastConversionExpr(expr, toType, conv, conv2));
|
|
|
|
}
|
|
|
|
Expr *ExprRewriter::buildObjCBridgeExpr(Expr *expr, Type toType,
|
|
ConstraintLocatorBuilder locator) {
|
|
Type fromType = cs.getType(expr);
|
|
|
|
// Bridged collection casts always succeed, so we treat them as
|
|
// collection "upcasts".
|
|
if ((fromType->isArray() && toType->isArray())
|
|
|| (ConstraintSystem::isDictionaryType(fromType)
|
|
&& ConstraintSystem::isDictionaryType(toType))
|
|
|| (ConstraintSystem::isSetType(fromType)
|
|
&& ConstraintSystem::isSetType(toType))) {
|
|
return buildCollectionUpcastExpr(expr, toType, /*bridged=*/true, locator);
|
|
}
|
|
|
|
// Bridging from a Swift type to an Objective-C class type.
|
|
if (toType->isAnyObject() ||
|
|
(fromType->getRValueType()->isPotentiallyBridgedValueType() &&
|
|
(toType->isBridgeableObjectType() || toType->isExistentialType()))) {
|
|
// Bridging to Objective-C.
|
|
Expr *objcExpr = bridgeToObjectiveC(expr, toType);
|
|
if (!objcExpr)
|
|
return nullptr;
|
|
|
|
// We might have a coercion of a Swift type to a CF type toll-free
|
|
// bridged to Objective-C.
|
|
//
|
|
// FIXME: Ideally we would instead have already recorded a restriction
|
|
// when solving the constraint, and we wouldn't need to duplicate this
|
|
// part of coerceToType() here.
|
|
if (auto foreignClass = toType->getClassOrBoundGenericClass()) {
|
|
if (foreignClass->getForeignClassKind() ==
|
|
ClassDecl::ForeignKind::CFType) {
|
|
return cs.cacheType(new (ctx)
|
|
ForeignObjectConversionExpr(objcExpr, toType));
|
|
}
|
|
}
|
|
|
|
return coerceToType(objcExpr, toType, locator);
|
|
}
|
|
|
|
// Bridging from an Objective-C class type to a Swift type.
|
|
return forceBridgeFromObjectiveC(expr, toType);
|
|
}
|
|
|
|
Expr *ExprRewriter::coerceExistential(Expr *expr, Type toType,
|
|
ConstraintLocatorBuilder locator) {
|
|
Type fromType = cs.getType(expr);
|
|
Type fromInstanceType = fromType;
|
|
Type toInstanceType = toType;
|
|
|
|
// For existential-to-existential coercions, open the source existential.
|
|
Type openedFromType;
|
|
if (fromType->isAnyExistentialType()) {
|
|
openedFromType = ExistentialArchetypeType::getAny(fromType->getCanonicalType());
|
|
}
|
|
|
|
Type openedFromInstanceType = openedFromType;
|
|
|
|
// Look through metatypes.
|
|
while (fromInstanceType->is<AnyMetatypeType>() &&
|
|
toInstanceType->is<ExistentialMetatypeType>()) {
|
|
fromInstanceType = fromInstanceType->getMetatypeInstanceType();
|
|
if (openedFromInstanceType)
|
|
openedFromInstanceType = openedFromInstanceType->getMetatypeInstanceType();
|
|
toInstanceType = toInstanceType->getMetatypeInstanceType();
|
|
}
|
|
|
|
/// Collect the conformances for all the protocols of an existential type.
|
|
/// If the source type is also existential, we don't want to check conformance
|
|
/// because most protocols do not conform to themselves -- however we still
|
|
/// allow the conversion here, except the ErasureExpr ends up with trivial
|
|
/// conformances.
|
|
|
|
// Use the requirements of any parameterized protocols to build out fake
|
|
// argument conversions that can be used to infer opaque types.
|
|
SmallVector<CollectionUpcastConversionExpr::ConversionPair, 4> argConversions;
|
|
|
|
auto fromConstraintType = fromInstanceType;
|
|
if (auto existential = fromConstraintType->getAs<ExistentialType>())
|
|
fromConstraintType = existential->getConstraintType();
|
|
|
|
auto toConstraintType = toInstanceType;
|
|
if (auto existential = toConstraintType->getAs<ExistentialType>())
|
|
toConstraintType = existential->getConstraintType();
|
|
|
|
auto fromPPT = fromConstraintType->getAs<ParameterizedProtocolType>();
|
|
auto toPPT = toConstraintType->getAs<ParameterizedProtocolType>();
|
|
|
|
if (fromPPT && toPPT) {
|
|
assert(fromPPT->getArgs().size() >= toPPT->getArgs().size());
|
|
for (unsigned i = 0; i < toPPT->getArgs().size(); ++i) {
|
|
auto firstTy = fromPPT->getArgs()[i];
|
|
auto secondTy = toPPT->getArgs()[i];
|
|
auto conv =
|
|
buildOpaqueElementConversion(*this, expr->getLoc(),
|
|
firstTy,
|
|
secondTy,
|
|
/*bridged*/ false,
|
|
locator, i);
|
|
argConversions.push_back(conv);
|
|
}
|
|
} else if ((fromPPT || toPPT) &&
|
|
!fromInstanceType->isExistentialType()) {
|
|
auto parameterized = fromConstraintType;
|
|
auto base = toConstraintType;
|
|
if (toPPT)
|
|
std::swap(parameterized, base);
|
|
|
|
SmallVector<Requirement, 4> reqs;
|
|
parameterized->castTo<ParameterizedProtocolType>()
|
|
->getRequirements(base, reqs);
|
|
|
|
for (unsigned i = 0; i < reqs.size(); ++i) {
|
|
const auto &req = reqs[i];
|
|
assert(req.getKind() == RequirementKind::SameType);
|
|
auto conv =
|
|
buildOpaqueElementConversion(*this, expr->getLoc(),
|
|
req.getFirstType(),
|
|
req.getSecondType(),
|
|
/*bridged*/ false,
|
|
locator, i);
|
|
argConversions.push_back(conv);
|
|
}
|
|
}
|
|
|
|
// For existential-to-existential coercions, open the source existential.
|
|
if (openedFromType) {
|
|
auto *archetypeVal = cs.cacheType(
|
|
new (ctx) OpaqueValueExpr(expr->getSourceRange(), openedFromType));
|
|
|
|
auto conformances =
|
|
collectExistentialConformances(openedFromInstanceType->getCanonicalType(),
|
|
toInstanceType->getCanonicalType(),
|
|
/*allowMissing=*/true);
|
|
|
|
auto *result = cs.cacheType(ErasureExpr::create(ctx, archetypeVal, toType,
|
|
conformances,
|
|
argConversions));
|
|
return cs.cacheType(
|
|
new (ctx) OpenExistentialExpr(expr, archetypeVal, result,
|
|
cs.getType(result)));
|
|
}
|
|
|
|
// Load tuples with lvalue elements.
|
|
if (auto tupleType = fromType->getAs<TupleType>()) {
|
|
if (tupleType->hasLValueType()) {
|
|
expr = cs.coerceToRValue(expr);
|
|
}
|
|
}
|
|
|
|
auto conformances =
|
|
collectExistentialConformances(fromInstanceType->getCanonicalType(),
|
|
toInstanceType->getCanonicalType(),
|
|
/*allowMissing=*/true);
|
|
|
|
return cs.cacheType(ErasureExpr::create(ctx, expr, toType,
|
|
conformances, argConversions));
|
|
}
|
|
|
|
Expr *ConstraintSystem::addImplicitLoadExpr(Expr *expr) {
|
|
return TypeChecker::addImplicitLoadExpr(
|
|
getASTContext(), expr, [this](Expr *expr) { return getType(expr); },
|
|
[this](Expr *expr, Type type) { setType(expr, type); });
|
|
}
|
|
|
|
Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
|
|
ConstraintLocatorBuilder locator) {
|
|
ASSERT(toType && !toType->hasError() && !toType->hasUnresolvedType() &&
|
|
!toType->hasTypeVariableOrPlaceholder());
|
|
|
|
// Diagnose conversions to invalid function types that couldn't be performed
|
|
// beforehand because of placeholders.
|
|
if (auto *fnTy = toType->getAs<FunctionType>()) {
|
|
auto contextTy = cs.getContextualType(expr, /*forConstraint=*/false);
|
|
if (cs.getConstraintLocator(locator)->isForContextualType() && contextTy &&
|
|
contextTy->hasPlaceholder()) {
|
|
bool hadError = TypeChecker::diagnoseInvalidFunctionType(
|
|
fnTy, expr->getLoc(), std::nullopt, dc, std::nullopt);
|
|
if (hadError)
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// The type we're converting from.
|
|
Type fromType = cs.getType(expr);
|
|
|
|
// If the types are already equivalent, we don't have to do anything.
|
|
if (fromType->isEqual(toType))
|
|
return expr;
|
|
|
|
// If the solver recorded what we should do here, just do it immediately.
|
|
auto knownRestriction = solution.ConstraintRestrictions.find(
|
|
{ fromType->getCanonicalType(),
|
|
toType->getCanonicalType() });
|
|
if (knownRestriction != solution.ConstraintRestrictions.end()) {
|
|
switch (knownRestriction->second) {
|
|
case ConversionRestrictionKind::DeepEquality: {
|
|
// HACK: Fix problem related to Swift 4 mode (with assertions),
|
|
// since Swift 4 mode allows passing arguments with extra parens
|
|
// to parameters which don't expect them, it should be supported
|
|
// by "deep equality" type - Optional<T> e.g.
|
|
// ```swift
|
|
// func foo(_: (() -> Void)?) {}
|
|
// func bar() -> ((()) -> Void)? { return nil }
|
|
// foo(bar) // This expression should compile in Swift 3 mode
|
|
// ```
|
|
//
|
|
// See also: https://github.com/apple/swift/issues/49345
|
|
if (ctx.isSwiftVersionAtLeast(4) &&
|
|
!ctx.isSwiftVersionAtLeast(5)) {
|
|
auto obj1 = fromType->getOptionalObjectType();
|
|
auto obj2 = toType->getOptionalObjectType();
|
|
|
|
if (obj1 && obj2) {
|
|
auto *fn1 = obj1->getAs<AnyFunctionType>();
|
|
auto *fn2 = obj2->getAs<AnyFunctionType>();
|
|
|
|
if (fn1 && fn2) {
|
|
auto params1 = fn1->getParams();
|
|
auto params2 = fn2->getParams();
|
|
|
|
// This handles situations like argument: (()), parameter: ().
|
|
if (params1.size() == 1 && params2.empty()) {
|
|
auto tupleTy = params1.front().getOldType()->getAs<TupleType>();
|
|
if (tupleTy && tupleTy->getNumElements() == 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// `any Sendable` -> `Any` conversion is allowed in generic
|
|
// argument positions.
|
|
{
|
|
auto erasedFromType = fromType->stripConcurrency(
|
|
/*recursive=*/true, /*dropGlobalActor=*/false);
|
|
auto erasedToType = toType->stripConcurrency(
|
|
/*recursive=*/true, /*dropGlobalActor=*/false);
|
|
|
|
if (erasedFromType->isEqual(erasedToType))
|
|
return cs.cacheType(new (ctx) UnsafeCastExpr(expr, toType));
|
|
}
|
|
|
|
auto &err = llvm::errs();
|
|
err << "fromType->getCanonicalType() = ";
|
|
fromType->getCanonicalType()->dump(err);
|
|
err << "toType->getCanonicalType() = ";
|
|
toType->getCanonicalType()->dump(err);
|
|
llvm_unreachable("Should be handled above");
|
|
}
|
|
|
|
case ConversionRestrictionKind::Superclass:
|
|
case ConversionRestrictionKind::ExistentialMetatypeToMetatype:
|
|
return coerceSuperclass(expr, toType);
|
|
|
|
case ConversionRestrictionKind::Existential:
|
|
case ConversionRestrictionKind::MetatypeToExistentialMetatype: {
|
|
auto coerced = coerceExistential(expr, toType, locator);
|
|
diagnoseExistentialErasureOf(expr, coerced, locator);
|
|
return coerced;
|
|
}
|
|
|
|
case ConversionRestrictionKind::ClassMetatypeToAnyObject: {
|
|
assert(ctx.LangOpts.EnableObjCInterop &&
|
|
"metatypes can only be cast to objects w/ objc runtime!");
|
|
return cs.cacheType(new (ctx) ClassMetatypeToObjectExpr(expr, toType));
|
|
}
|
|
case ConversionRestrictionKind::ExistentialMetatypeToAnyObject: {
|
|
assert(ctx.LangOpts.EnableObjCInterop &&
|
|
"metatypes can only be cast to objects w/ objc runtime!");
|
|
return cs.cacheType(new (ctx)
|
|
ExistentialMetatypeToObjectExpr(expr, toType));
|
|
}
|
|
case ConversionRestrictionKind::ProtocolMetatypeToProtocolClass: {
|
|
return cs.cacheType(new (ctx) ProtocolMetatypeToObjectExpr(expr, toType));
|
|
}
|
|
|
|
case ConversionRestrictionKind::ValueToOptional: {
|
|
auto toGenericType = toType->castTo<BoundGenericType>();
|
|
assert(toGenericType->getDecl()->isOptionalDecl());
|
|
TypeChecker::requireOptionalIntrinsics(ctx, expr->getLoc());
|
|
|
|
Type valueType = toGenericType->getGenericArgs()[0];
|
|
expr = coerceToType(expr, valueType, locator);
|
|
if (!expr) return nullptr;
|
|
|
|
auto *result =
|
|
cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, toType));
|
|
diagnoseOptionalInjection(result, locator);
|
|
return result;
|
|
}
|
|
|
|
case ConversionRestrictionKind::OptionalToOptional:
|
|
return coerceOptionalToOptional(expr, toType, locator);
|
|
|
|
case ConversionRestrictionKind::ArrayUpcast: {
|
|
// Build the value conversion.
|
|
return buildCollectionUpcastExpr(expr, toType, /*bridged=*/false,
|
|
locator);
|
|
}
|
|
|
|
case ConversionRestrictionKind::HashableToAnyHashable: {
|
|
// We want to check conformance on the rvalue, as that's what has
|
|
// the Hashable conformance
|
|
expr = cs.coerceToRValue(expr);
|
|
|
|
// Find the conformance of the source type to Hashable.
|
|
auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable);
|
|
auto conformance = checkConformance(cs.getType(expr), hashable);
|
|
assert(conformance && "must conform to Hashable");
|
|
|
|
return cs.cacheType(
|
|
new (ctx) AnyHashableErasureExpr(expr, toType, conformance));
|
|
}
|
|
|
|
case ConversionRestrictionKind::DictionaryUpcast: {
|
|
// Build the value conversion.
|
|
return buildCollectionUpcastExpr(expr, toType, /*bridged=*/false,
|
|
locator);
|
|
}
|
|
|
|
case ConversionRestrictionKind::SetUpcast: {
|
|
// Build the value conversion.
|
|
return buildCollectionUpcastExpr(expr, toType, /*bridged=*/false, locator);
|
|
}
|
|
|
|
case ConversionRestrictionKind::InoutToPointer:
|
|
case ConversionRestrictionKind::InoutToCPointer: {
|
|
bool isOptional = false;
|
|
Type unwrappedTy = toType;
|
|
if (Type unwrapped = toType->getOptionalObjectType()) {
|
|
isOptional = true;
|
|
unwrappedTy = unwrapped;
|
|
}
|
|
PointerTypeKind pointerKind;
|
|
auto toEltType = unwrappedTy->getAnyPointerElementType(pointerKind);
|
|
assert(toEltType && "not a pointer type?"); (void) toEltType;
|
|
|
|
TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc());
|
|
Expr *result =
|
|
cs.cacheType(new (ctx) InOutToPointerExpr(expr, unwrappedTy));
|
|
if (isOptional)
|
|
result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, toType));
|
|
return result;
|
|
}
|
|
|
|
case ConversionRestrictionKind::ArrayToPointer:
|
|
case ConversionRestrictionKind::ArrayToCPointer: {
|
|
bool isOptional = false;
|
|
Type unwrappedTy = toType;
|
|
if (Type unwrapped = toType->getOptionalObjectType()) {
|
|
isOptional = true;
|
|
unwrappedTy = unwrapped;
|
|
}
|
|
|
|
TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc());
|
|
Expr *result =
|
|
cs.cacheType(new (ctx) ArrayToPointerExpr(expr, unwrappedTy));
|
|
if (isOptional)
|
|
result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, toType));
|
|
return result;
|
|
}
|
|
|
|
case ConversionRestrictionKind::StringToPointer: {
|
|
bool isOptional = false;
|
|
Type unwrappedTy = toType;
|
|
if (Type unwrapped = toType->getOptionalObjectType()) {
|
|
isOptional = true;
|
|
unwrappedTy = unwrapped;
|
|
}
|
|
|
|
TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc());
|
|
Expr *result =
|
|
cs.cacheType(new (ctx) StringToPointerExpr(expr, unwrappedTy));
|
|
if (isOptional)
|
|
result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, toType));
|
|
return result;
|
|
}
|
|
|
|
case ConversionRestrictionKind::PointerToPointer:
|
|
case ConversionRestrictionKind::PointerToCPointer: {
|
|
TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc());
|
|
Type unwrappedToTy = toType->getOptionalObjectType();
|
|
|
|
// Optional to optional.
|
|
if (Type unwrappedFromTy = cs.getType(expr)->getOptionalObjectType()) {
|
|
assert(unwrappedToTy && "converting optional to non-optional");
|
|
Expr *boundOptional = cs.cacheType(
|
|
new (ctx) BindOptionalExpr(expr, SourceLoc(),
|
|
/*depth*/ 0, unwrappedFromTy));
|
|
Expr *converted = cs.cacheType(
|
|
new (ctx) PointerToPointerExpr(boundOptional, unwrappedToTy));
|
|
Expr *rewrapped =
|
|
cs.cacheType(new (ctx) InjectIntoOptionalExpr(converted, toType));
|
|
return cs.cacheType(new (ctx)
|
|
OptionalEvaluationExpr(rewrapped, toType));
|
|
}
|
|
|
|
// Non-optional to optional.
|
|
if (unwrappedToTy) {
|
|
Expr *converted =
|
|
cs.cacheType(new (ctx) PointerToPointerExpr(expr, unwrappedToTy));
|
|
return cs.cacheType(new (ctx)
|
|
InjectIntoOptionalExpr(converted, toType));
|
|
}
|
|
|
|
// Non-optional to non-optional.
|
|
return cs.cacheType(new (ctx) PointerToPointerExpr(expr, toType));
|
|
}
|
|
|
|
case ConversionRestrictionKind::CFTollFreeBridgeToObjC: {
|
|
auto foreignClass = fromType->getClassOrBoundGenericClass();
|
|
auto objcType = foreignClass->getAttrs().getAttribute<ObjCBridgedAttr>()
|
|
->getObjCClass()->getDeclaredInterfaceType();
|
|
auto asObjCClass =
|
|
cs.cacheType(new (ctx) ForeignObjectConversionExpr(expr, objcType));
|
|
return coerceToType(asObjCClass, toType, locator);
|
|
}
|
|
|
|
case ConversionRestrictionKind::ObjCTollFreeBridgeToCF: {
|
|
auto foreignClass = toType->getClassOrBoundGenericClass();
|
|
auto objcType = foreignClass->getAttrs().getAttribute<ObjCBridgedAttr>()
|
|
->getObjCClass()->getDeclaredInterfaceType();
|
|
Expr *result = coerceToType(expr, objcType, locator);
|
|
if (!result)
|
|
return nullptr;
|
|
|
|
return cs.cacheType(new (ctx)
|
|
ForeignObjectConversionExpr(result, toType));
|
|
}
|
|
|
|
case ConversionRestrictionKind::CGFloatToDouble:
|
|
case ConversionRestrictionKind::DoubleToCGFloat: {
|
|
DeclName name(ctx, DeclBaseName::createConstructor(), Identifier());
|
|
|
|
ConstructorDecl *decl = nullptr;
|
|
SmallVector<ValueDecl *, 2> candidates;
|
|
dc->lookupQualified(toType->getAnyNominal(),
|
|
DeclNameRef(name), SourceLoc(),
|
|
NL_QualifiedDefault, candidates);
|
|
for (auto *candidate : candidates) {
|
|
auto *ctor = cast<ConstructorDecl>(candidate);
|
|
auto fnType = ctor->getMethodInterfaceType()->castTo<FunctionType>();
|
|
if (fnType->getNumParams() == 1 &&
|
|
fnType->getParams()[0].getPlainType()->isEqual(fromType) &&
|
|
fnType->getResult()->isEqual(toType)) {
|
|
decl = ctor;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (decl == nullptr) {
|
|
ctx.Diags.diagnose(expr->getLoc(), diag::broken_stdlib_type,
|
|
toType->getAnyNominal()->getName().str());
|
|
auto *errorExpr = new (ctx) ErrorExpr(SourceRange(), toType);
|
|
cs.setType(errorExpr, toType);
|
|
|
|
return errorExpr;
|
|
}
|
|
|
|
auto *ctorRefExpr = new (ctx) DeclRefExpr(decl, DeclNameLoc(), /*Implicit=*/true);
|
|
ctorRefExpr->setType(decl->getInterfaceType());
|
|
auto *typeExpr = TypeExpr::createImplicit(toType, ctx);
|
|
auto *innerCall = ConstructorRefCallExpr::create(ctx, ctorRefExpr, typeExpr,
|
|
decl->getMethodInterfaceType());
|
|
cs.cacheExprTypes(innerCall);
|
|
|
|
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {cs.coerceToRValue(expr)});
|
|
auto *outerCall = CallExpr::createImplicit(ctx, innerCall, argList);
|
|
outerCall->setType(toType);
|
|
cs.setType(outerCall, toType);
|
|
|
|
return outerCall;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Use an opaque type to abstract a value of the underlying concrete type.
|
|
// The full check here would be that `toType` and `fromType` are structurally
|
|
// equal except in any position where `toType` has an opaque archetype. The
|
|
// below is just an approximate check since the above would be expensive to
|
|
// verify and still relies on the type checker ensuing `fromType` is
|
|
// compatible with any opaque archetypes.
|
|
if (toType->getCanonicalType()->hasOpaqueArchetype() &&
|
|
cs.getConstraintLocator(locator)->isForContextualType()) {
|
|
// Find the opaque type declaration. We need its generic signature.
|
|
OpaqueTypeDecl *opaqueDecl = nullptr;
|
|
bool found = toType->getCanonicalType().findIf([&](Type type) {
|
|
if (auto opaqueType = type->getAs<OpaqueTypeArchetypeType>()) {
|
|
opaqueDecl = opaqueType->getDecl();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
});
|
|
(void)found;
|
|
assert(found && "No opaque type archetype?");
|
|
|
|
// Compute the substitutions for the opaque type declaration.
|
|
auto opaqueLocator = solution.getConstraintSystem().getOpenOpaqueLocator(
|
|
locator, opaqueDecl);
|
|
SubstitutionMap substitutions = solution.computeSubstitutions(
|
|
opaqueDecl, opaqueDecl->getOpaqueInterfaceGenericSignature(),
|
|
opaqueLocator);
|
|
|
|
// If we don't have substitutions, this is an opaque archetype from
|
|
// another declaration being manipulated, and not an erasure of a
|
|
// concrete type to an opaque type inside its defining declaration.
|
|
if (!substitutions.empty()) {
|
|
// Compute the underlying type by replacing all opaque archetypes with
|
|
// the fixed type of their opened type.
|
|
auto underlyingType = toType.transformRec(
|
|
[&](TypeBase *type) -> std::optional<Type> {
|
|
if (auto *opaqueType = dyn_cast<OpaqueTypeArchetypeType>(type)) {
|
|
if (opaqueType->getDecl() == opaqueDecl) {
|
|
return opaqueType->getInterfaceType().subst(substitutions);
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
});
|
|
|
|
// Coerce the result expression to the underlying type.
|
|
// FIXME: Wrong locator?
|
|
auto *subExpr = coerceToType(expr, underlyingType, locator);
|
|
|
|
return cs.cacheType(
|
|
new (ctx) UnderlyingToOpaqueExpr(subExpr, toType, substitutions));
|
|
}
|
|
}
|
|
|
|
// Handle "from specific" coercions before "catch all" coercions.
|
|
auto desugaredFromType = fromType->getDesugaredType();
|
|
switch (desugaredFromType->getKind()) {
|
|
// Coercions from an lvalue: load or perform implicit address-of. We perform
|
|
// these coercions first because they are often the first step in a multi-step
|
|
// coercion.
|
|
case TypeKind::LValue: {
|
|
auto fromLValue = cast<LValueType>(desugaredFromType);
|
|
|
|
auto injectUnsafeLValueCast = [&](Type fromObjType,
|
|
Type toObjType) -> Expr * {
|
|
auto restriction = solution.getConversionRestriction(
|
|
fromObjType->getCanonicalType(), toObjType->getCanonicalType());
|
|
ASSERT(restriction == ConversionRestrictionKind::DeepEquality);
|
|
return cs.cacheType(
|
|
new (ctx) ABISafeConversionExpr(expr, LValueType::get(toObjType)));
|
|
};
|
|
|
|
// @lvalue <A> -> @lvalue <B> is only allowed if there is a
|
|
// deep equality conversion restriction between the types.
|
|
// This supports `any Sendable` -> `Any` conversion in generic
|
|
// argument positions.
|
|
if (auto *toLValue = toType->getAs<LValueType>()) {
|
|
return injectUnsafeLValueCast(fromLValue->getObjectType(),
|
|
toLValue->getObjectType());
|
|
}
|
|
|
|
auto toIO = toType->getAs<InOutType>();
|
|
if (!toIO)
|
|
return coerceToType(cs.addImplicitLoadExpr(expr), toType, locator);
|
|
|
|
// @lvalue <A> -> inout <B> has to use an unsafe cast <A> -> <B>:
|
|
// @lvalue <A> <cast to> @lvalue B -> inout B.
|
|
//
|
|
// This can happen due to any Sendable -> Any conversion in generic
|
|
// argument positions. We need to inject a cast to get @l-value to
|
|
// match `inout` type exactly.
|
|
if (!toIO->getObjectType()->isEqual(fromLValue->getObjectType())) {
|
|
expr = injectUnsafeLValueCast(fromLValue->getObjectType(),
|
|
toIO->getObjectType());
|
|
}
|
|
|
|
// In an 'inout' operator like "i += 1", the operand is converted from
|
|
// an implicit lvalue to an inout argument.
|
|
return cs.cacheType(new (ctx) InOutExpr(expr->getStartLoc(), expr,
|
|
toIO->getObjectType(),
|
|
/*isImplicit*/ true));
|
|
}
|
|
|
|
case TypeKind::InOut: {
|
|
auto *inOutExpr = getAsExpr<InOutExpr>(expr);
|
|
if (!inOutExpr)
|
|
break;
|
|
|
|
// If there is an `any Sendable` -> `Any` mismatch here,
|
|
// the conversion should be performed on l-value and the
|
|
// address taken from that. This is something that is already
|
|
// done as part of implicit `inout` injection for operators
|
|
// and could be reused here.
|
|
return coerceToType(inOutExpr->getSubExpr(), toType, locator);
|
|
}
|
|
|
|
case TypeKind::Pack:
|
|
case TypeKind::PackElement: {
|
|
llvm_unreachable("Unimplemented!");
|
|
}
|
|
|
|
case TypeKind::PackExpansion: {
|
|
auto toExpansionType = toType->getAs<PackExpansionType>();
|
|
auto *expansion = dyn_cast<PackExpansionExpr>(expr);
|
|
|
|
auto *elementEnv = expansion->getGenericEnvironment();
|
|
auto toElementType = elementEnv->mapContextualPackTypeIntoElementContext(
|
|
toExpansionType->getPatternType());
|
|
|
|
auto *pattern = coerceToType(expansion->getPatternExpr(),
|
|
toElementType, locator);
|
|
auto *packEnv = cs.DC->getGenericEnvironmentOfContext();
|
|
auto patternType = packEnv->mapElementTypeIntoPackContext(toElementType);
|
|
auto shapeType = toExpansionType->getCountType();
|
|
auto expansionTy = PackExpansionType::get(patternType, shapeType);
|
|
|
|
expansion->setPatternExpr(pattern);
|
|
expansion->setType(expansionTy);
|
|
return cs.cacheType(expansion);
|
|
}
|
|
|
|
case TypeKind::BuiltinTuple:
|
|
llvm_unreachable("BuiltinTupleType should not show up here");
|
|
|
|
// Coerce from a tuple to a tuple.
|
|
case TypeKind::Tuple: {
|
|
auto fromTuple = cast<TupleType>(desugaredFromType);
|
|
auto toTuple = toType->getAs<TupleType>();
|
|
if (!toTuple)
|
|
break;
|
|
|
|
if (fromTuple->hasLValueType() && !toTuple->hasLValueType())
|
|
return coerceToType(cs.coerceToRValue(expr), toType, locator);
|
|
|
|
SmallVector<unsigned, 4> sources;
|
|
if (!computeTupleShuffle(fromTuple, toTuple, sources)) {
|
|
return coerceTupleToTuple(expr, fromTuple, toTuple,
|
|
locator, sources);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TypeKind::PrimaryArchetype:
|
|
case TypeKind::ExistentialArchetype:
|
|
case TypeKind::OpaqueTypeArchetype:
|
|
case TypeKind::PackArchetype:
|
|
case TypeKind::ElementArchetype:
|
|
if (!cast<ArchetypeType>(desugaredFromType)->requiresClass())
|
|
break;
|
|
LLVM_FALLTHROUGH;
|
|
|
|
// Coercion from a subclass to a superclass.
|
|
//
|
|
// FIXME: Can we rig things up so that we always have a Superclass
|
|
// conversion restriction in this case?
|
|
case TypeKind::DynamicSelf:
|
|
case TypeKind::BoundGenericClass:
|
|
case TypeKind::Class: {
|
|
if (!toType->getClassOrBoundGenericClass())
|
|
break;
|
|
for (auto fromSuperClass = fromType->getSuperclass();
|
|
fromSuperClass;
|
|
fromSuperClass = fromSuperClass->getSuperclass()) {
|
|
if (fromSuperClass->isEqual(toType)) {
|
|
return coerceSuperclass(expr, toType);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Coercion from one function type to another, this produces a
|
|
// FunctionConversionExpr in its full generality.
|
|
case TypeKind::Function: {
|
|
auto fromFunc = cast<FunctionType>(desugaredFromType);
|
|
auto toFunc = toType->getAs<FunctionType>();
|
|
if (!toFunc)
|
|
break;
|
|
|
|
// Default argument generator must return escaping functions. Therefore, we
|
|
// leave an explicit escape to noescape cast here such that SILGen can skip
|
|
// the cast and emit a code for the escaping function.
|
|
bool isInDefaultArgumentContext = false;
|
|
if (auto initializerCtx = dyn_cast<Initializer>(dc))
|
|
isInDefaultArgumentContext = (initializerCtx->getInitializerKind() ==
|
|
InitializerKind::DefaultArgument);
|
|
auto toEI = toFunc->getExtInfo();
|
|
assert(toType->is<FunctionType>());
|
|
|
|
// Handle implicit conversions between non-@differentiable and
|
|
// @differentiable functions.
|
|
{
|
|
auto fromEI = fromFunc->getExtInfo();
|
|
auto isFromDifferentiable = fromEI.isDifferentiable();
|
|
auto isToDifferentiable = toEI.isDifferentiable();
|
|
// Handle implicit conversion from @differentiable.
|
|
if (isFromDifferentiable && !isToDifferentiable) {
|
|
fromFunc = fromFunc->getWithoutDifferentiability()
|
|
->castTo<FunctionType>();
|
|
switch (fromEI.getDifferentiabilityKind()) {
|
|
// TODO: Ban `Normal` and `Forward` cases.
|
|
case DifferentiabilityKind::Normal:
|
|
case DifferentiabilityKind::Forward:
|
|
case DifferentiabilityKind::Reverse:
|
|
expr = cs.cacheType(new (ctx)
|
|
DifferentiableFunctionExtractOriginalExpr(expr, fromFunc));
|
|
break;
|
|
case DifferentiabilityKind::Linear:
|
|
expr = cs.cacheType(new (ctx)
|
|
LinearFunctionExtractOriginalExpr(expr, fromFunc));
|
|
break;
|
|
case DifferentiabilityKind::NonDifferentiable:
|
|
llvm_unreachable("Cannot be NonDifferentiable");
|
|
}
|
|
}
|
|
// Handle implicit conversion from non-@differentiable to @differentiable.
|
|
maybeDiagnoseUnsupportedDifferentiableConversion(cs, expr, toFunc);
|
|
if (!isFromDifferentiable && isToDifferentiable) {
|
|
auto newEI =
|
|
fromEI.intoBuilder()
|
|
.withDifferentiabilityKind(toEI.getDifferentiabilityKind())
|
|
.build();
|
|
SmallVector<AnyFunctionType::Param, 4> params(fromFunc->getParams());
|
|
assert(params.size() == toFunc->getParams().size() &&
|
|
"unexpected @differentiable conversion");
|
|
// Propagate @noDerivate from target function type
|
|
for (auto paramAndIndex : llvm::enumerate(toFunc->getParams())) {
|
|
if (!paramAndIndex.value().isNoDerivative())
|
|
continue;
|
|
|
|
auto ¶m = params[paramAndIndex.index()];
|
|
param =
|
|
param.withFlags(param.getParameterFlags().withNoDerivative(true));
|
|
}
|
|
|
|
fromFunc = FunctionType::get(params, fromFunc->getResult(), newEI);
|
|
switch (toEI.getDifferentiabilityKind()) {
|
|
// TODO: Ban `Normal` and `Forward` cases.
|
|
case DifferentiabilityKind::Normal:
|
|
case DifferentiabilityKind::Forward:
|
|
case DifferentiabilityKind::Reverse:
|
|
expr = cs.cacheType(new (ctx)
|
|
DifferentiableFunctionExpr(expr, fromFunc));
|
|
break;
|
|
case DifferentiabilityKind::Linear:
|
|
expr = cs.cacheType(new (ctx) LinearFunctionExpr(expr, fromFunc));
|
|
break;
|
|
case DifferentiabilityKind::NonDifferentiable:
|
|
llvm_unreachable("Cannot be NonDifferentiable");
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we have a ClosureExpr, then we can safely propagate @Sendable
|
|
// to the closure without invalidating prior analysis.
|
|
auto fromEI = fromFunc->getExtInfo();
|
|
if (toEI.isSendable() && !fromEI.isSendable()) {
|
|
auto newFromFuncType = fromFunc->withExtInfo(fromEI.withSendable());
|
|
if (applyTypeToClosureExpr(cs, expr, newFromFuncType)) {
|
|
fromFunc = newFromFuncType->castTo<FunctionType>();
|
|
|
|
// Propagating the 'concurrent' bit might have satisfied the entire
|
|
// conversion. If so, we're done, otherwise keep converting.
|
|
if (fromFunc->isEqual(toType))
|
|
return expr;
|
|
}
|
|
}
|
|
|
|
// If we have a ClosureExpr, then we can safely propagate a global actor
|
|
// to the closure if it's not explicitly marked as `@concurrent` without
|
|
// invalidating prior analysis.
|
|
fromEI = fromFunc->getExtInfo();
|
|
if (toEI.getGlobalActor() && !fromEI.getGlobalActor() &&
|
|
!isClosureMarkedAsConcurrent(expr)) {
|
|
auto newFromFuncType =
|
|
fromFunc->withExtInfo(fromEI.withGlobalActor(toEI.getGlobalActor()));
|
|
if (applyTypeToClosureExpr(cs, expr, newFromFuncType)) {
|
|
fromFunc = newFromFuncType->castTo<FunctionType>();
|
|
|
|
// Propagating the global actor bit might have satisfied the entire
|
|
// conversion. If so, we're done, otherwise keep converting.
|
|
if (fromFunc->isEqual(toType))
|
|
return expr;
|
|
}
|
|
}
|
|
|
|
/// Whether the given effect is polymorphic at this location.
|
|
auto isEffectPolymorphic = [&](EffectKind kind) -> bool {
|
|
if (!locator.endsWith<LocatorPathElt::ApplyArgToParam>())
|
|
return false;
|
|
|
|
if (auto *call = getAsExpr<ApplyExpr>(locator.getAnchor())) {
|
|
if (auto *declRef = dyn_cast<DeclRefExpr>(call->getFn())) {
|
|
if (auto *fn = dyn_cast<AbstractFunctionDecl>(declRef->getDecl()))
|
|
return fn->hasPolymorphicEffect(kind);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
// If we have a ClosureExpr, and we can safely propagate 'async' to the
|
|
// closure, do that here.
|
|
fromEI = fromFunc->getExtInfo();
|
|
bool shouldPropagateAsync =
|
|
!isEffectPolymorphic(EffectKind::Async) || closureInheritsActorContext(expr);
|
|
if (toEI.isAsync() && !fromEI.isAsync() && shouldPropagateAsync) {
|
|
auto newFromFuncType = fromFunc->withExtInfo(fromEI.withAsync());
|
|
if (applyTypeToClosureExpr(cs, expr, newFromFuncType)) {
|
|
fromFunc = newFromFuncType->castTo<FunctionType>();
|
|
|
|
// Propagating 'async' might have satisfied the entire conversion.
|
|
// If so, we're done, otherwise keep converting.
|
|
if (fromFunc->isEqual(toType))
|
|
return expr;
|
|
}
|
|
}
|
|
|
|
// If we have a ClosureExpr, then we can safely propagate the 'no escape'
|
|
// bit to the closure without invalidating prior analysis.
|
|
fromEI = fromFunc->getExtInfo();
|
|
if (toEI.isNoEscape() && !fromEI.isNoEscape()) {
|
|
auto newFromFuncType = fromFunc->withExtInfo(fromEI.withNoEscape());
|
|
if (!isInDefaultArgumentContext &&
|
|
applyTypeToClosureExpr(cs, expr, newFromFuncType)) {
|
|
fromFunc = newFromFuncType->castTo<FunctionType>();
|
|
// Propagating the 'no escape' bit might have satisfied the entire
|
|
// conversion. If so, we're done, otherwise keep converting.
|
|
if (fromFunc->isEqual(toType))
|
|
return expr;
|
|
} else if (isInDefaultArgumentContext) {
|
|
// First apply the conversion *without* noescape attribute.
|
|
if (!newFromFuncType->isEqual(toType)) {
|
|
auto escapingToFuncTy =
|
|
toFunc->withExtInfo(toEI.withNoEscape(false));
|
|
maybeDiagnoseUnsupportedFunctionConversion(cs, expr, toFunc);
|
|
expr = cs.cacheType(
|
|
new (ctx) FunctionConversionExpr(expr, escapingToFuncTy));
|
|
}
|
|
// Apply an explicit function conversion *only* for the escape to
|
|
// noescape conversion. This conversion will be stripped by the
|
|
// default argument generator. (We can't return a @noescape function)
|
|
auto newExpr =
|
|
cs.cacheType(new (ctx) FunctionConversionExpr(expr, toFunc));
|
|
return newExpr;
|
|
}
|
|
}
|
|
|
|
if (ctx.LangOpts.isDynamicActorIsolationCheckingEnabled()) {
|
|
// Passing a synchronous global actor-isolated function value and
|
|
// parameter that expects a synchronous non-isolated function type could
|
|
// require a runtime check to ensure that function is always called in
|
|
// expected context.
|
|
if (!toEI.getGlobalActor() && fromEI.getGlobalActor() &&
|
|
!toEI.isAsync()) {
|
|
// Runtime check is required when isolation function value
|
|
// is passed to an API that comes from a module that doesn't
|
|
// have full static concurrency checking enabled.
|
|
auto requiresRuntimeCheck = [&]() {
|
|
if (!locator.endsWith<LocatorPathElt::ApplyArgToParam>())
|
|
return false;
|
|
|
|
ConstraintLocator *calleeLoc = nullptr;
|
|
if (auto *call = getAsExpr<ApplyExpr>(locator.getAnchor())) {
|
|
calleeLoc = CalleeLocators[call];
|
|
} else {
|
|
calleeLoc =
|
|
solution.getCalleeLocator(cs.getConstraintLocator(locator));
|
|
}
|
|
|
|
auto overload = solution.getOverloadChoiceIfAvailable(calleeLoc);
|
|
if (!(overload && overload->choice.isDecl()))
|
|
return false;
|
|
|
|
auto *decl = overload->choice.getDecl();
|
|
// Function values passed to C/ObjC APIs are already thunked
|
|
// and that's where the check is going to go.
|
|
if (decl->hasClangNode())
|
|
return false;
|
|
|
|
auto declaredIn = decl->findImport(dc);
|
|
if (!declaredIn)
|
|
return false;
|
|
|
|
return !declaredIn->module.importedModule->isConcurrencyChecked();
|
|
};
|
|
|
|
if (requiresRuntimeCheck()) {
|
|
auto isolatedToType =
|
|
FunctionType::get(toFunc->getParams(), toFunc->getResult(),
|
|
toEI.withGlobalActor(fromEI.getGlobalActor()));
|
|
|
|
// Global actor might not be the only difference, let's introduce
|
|
// a function conversion first but with matching isolation.
|
|
expr = cs.cacheType(new (ctx)
|
|
FunctionConversionExpr(expr, isolatedToType));
|
|
|
|
return cs.cacheType(new (ctx)
|
|
ActorIsolationErasureExpr(expr, toType));
|
|
}
|
|
}
|
|
}
|
|
|
|
maybeDiagnoseUnsupportedFunctionConversion(cs, expr, toFunc);
|
|
|
|
return cs.cacheType(new (ctx) FunctionConversionExpr(expr, toType));
|
|
}
|
|
|
|
// Coercions from one metatype to another.
|
|
case TypeKind::Metatype: {
|
|
if (auto toMeta = toType->getAs<MetatypeType>())
|
|
return cs.cacheType(new (ctx) MetatypeConversionExpr(expr, toMeta));
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
// Coercions from metatype to objects.
|
|
case TypeKind::ExistentialMetatype: {
|
|
auto fromMeta = cast<AnyMetatypeType>(desugaredFromType);
|
|
if (toType->isAnyObject()) {
|
|
assert(ctx.LangOpts.EnableObjCInterop
|
|
&& "metatype-to-object conversion requires objc interop");
|
|
if (fromMeta->is<MetatypeType>()) {
|
|
assert(fromMeta->getInstanceType()->mayHaveSuperclass()
|
|
&& "metatype-to-object input should be a class metatype");
|
|
return cs.cacheType(new (ctx) ClassMetatypeToObjectExpr(expr, toType));
|
|
}
|
|
|
|
if (fromMeta->is<ExistentialMetatypeType>()) {
|
|
assert(fromMeta->getInstanceType()->getCanonicalType()
|
|
->getExistentialLayout().requiresClass()
|
|
&& "metatype-to-object input should be a class metatype");
|
|
return cs.cacheType(new (ctx)
|
|
ExistentialMetatypeToObjectExpr(expr, toType));
|
|
}
|
|
|
|
llvm_unreachable("unhandled metatype kind");
|
|
}
|
|
|
|
if (auto toClass = toType->getClassOrBoundGenericClass()) {
|
|
if (toClass->getName() == ctx.Id_Protocol
|
|
&& toClass->getModuleContext()->getName()
|
|
== ctx.Id_ObjectiveC) {
|
|
assert(ctx.LangOpts.EnableObjCInterop
|
|
&& "metatype-to-object conversion requires objc interop");
|
|
assert(fromMeta->is<MetatypeType>()
|
|
&& fromMeta->getInstanceType()->is<ProtocolType>()
|
|
&& "protocol-metatype-to-Protocol only works for single "
|
|
"protocols");
|
|
return cs.cacheType(new (ctx)
|
|
ProtocolMetatypeToObjectExpr(expr, toType));
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
#define SUGARED_TYPE(Name, Parent) case TypeKind::Name:
|
|
#define BUILTIN_TYPE(Name, Parent) case TypeKind::Name:
|
|
#define UNCHECKED_TYPE(Name, Parent) case TypeKind::Name:
|
|
#define ARTIFICIAL_TYPE(Name, Parent) case TypeKind::Name:
|
|
#define TYPE(Name, Parent)
|
|
#include "swift/AST/TypeNodes.def"
|
|
case TypeKind::Error:
|
|
case TypeKind::Module:
|
|
case TypeKind::Enum:
|
|
case TypeKind::Struct:
|
|
case TypeKind::Protocol:
|
|
case TypeKind::ProtocolComposition:
|
|
case TypeKind::ParameterizedProtocol:
|
|
case TypeKind::Existential:
|
|
case TypeKind::BoundGenericEnum:
|
|
case TypeKind::BoundGenericStruct:
|
|
case TypeKind::GenericFunction:
|
|
case TypeKind::GenericTypeParam:
|
|
case TypeKind::DependentMember:
|
|
case TypeKind::Integer:
|
|
break;
|
|
}
|
|
|
|
// Uninhabited types can be coerced to any other type via an UnreachableExpr.
|
|
if (fromType->isUninhabited())
|
|
return cs.cacheType(UnreachableExpr::create(ctx, expr, toType));
|
|
|
|
// "Catch all" coercions.
|
|
auto desugaredToType = toType->getDesugaredType();
|
|
switch (desugaredToType->getKind()) {
|
|
// Coercions from a type to an existential type.
|
|
case TypeKind::Existential:
|
|
case TypeKind::ExistentialMetatype:
|
|
case TypeKind::ProtocolComposition:
|
|
case TypeKind::ParameterizedProtocol:
|
|
case TypeKind::Protocol:
|
|
return coerceExistential(expr, toType, locator);
|
|
|
|
// Coercion to Optional<T>.
|
|
case TypeKind::BoundGenericEnum: {
|
|
auto toGenericType = cast<BoundGenericEnumType>(desugaredToType);
|
|
if (!toGenericType->getDecl()->isOptionalDecl())
|
|
break;
|
|
TypeChecker::requireOptionalIntrinsics(ctx, expr->getLoc());
|
|
|
|
if (cs.getType(expr)->getOptionalObjectType())
|
|
return coerceOptionalToOptional(expr, toType, locator);
|
|
|
|
Type valueType = toGenericType->getGenericArgs()[0];
|
|
expr = coerceToType(expr, valueType, locator);
|
|
if (!expr) return nullptr;
|
|
|
|
auto *result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, toType));
|
|
diagnoseOptionalInjection(result, locator);
|
|
return result;
|
|
}
|
|
|
|
case TypeKind::BoundGenericStruct: {
|
|
auto toStruct = cast<BoundGenericStructType>(desugaredToType);
|
|
if (!toStruct->isArray() && !toStruct->isDictionary())
|
|
break;
|
|
|
|
if (toStruct->getDecl() == cs.getType(expr)->getAnyNominal())
|
|
return buildCollectionUpcastExpr(expr, toType, /*bridged=*/false,
|
|
locator);
|
|
|
|
break;
|
|
}
|
|
|
|
#define SUGARED_TYPE(Name, Parent) case TypeKind::Name:
|
|
#define BUILTIN_TYPE(Name, Parent) case TypeKind::Name:
|
|
#define UNCHECKED_TYPE(Name, Parent) case TypeKind::Name:
|
|
#define ARTIFICIAL_TYPE(Name, Parent) case TypeKind::Name:
|
|
#define TYPE(Name, Parent)
|
|
#include "swift/AST/TypeNodes.def"
|
|
case TypeKind::Error:
|
|
case TypeKind::Module:
|
|
case TypeKind::Tuple:
|
|
case TypeKind::Enum:
|
|
case TypeKind::Struct:
|
|
case TypeKind::Class:
|
|
case TypeKind::BoundGenericClass:
|
|
case TypeKind::Metatype:
|
|
case TypeKind::DynamicSelf:
|
|
case TypeKind::PrimaryArchetype:
|
|
case TypeKind::ExistentialArchetype:
|
|
case TypeKind::OpaqueTypeArchetype:
|
|
case TypeKind::PackArchetype:
|
|
case TypeKind::ElementArchetype:
|
|
case TypeKind::GenericTypeParam:
|
|
case TypeKind::DependentMember:
|
|
case TypeKind::Function:
|
|
case TypeKind::GenericFunction:
|
|
case TypeKind::LValue:
|
|
case TypeKind::InOut:
|
|
case TypeKind::Pack:
|
|
case TypeKind::PackExpansion:
|
|
case TypeKind::PackElement:
|
|
case TypeKind::Integer:
|
|
break;
|
|
|
|
case TypeKind::BuiltinTuple:
|
|
llvm_unreachable("BuiltinTupleType should not show up here");
|
|
}
|
|
|
|
// Allow existential-to-supertype conversion if all protocol
|
|
// bounds are marker protocols. Normally this requires a
|
|
// conversion restriction but there are situations related
|
|
// to `@preconcurrency` where the `& Sendable` would be stripped
|
|
// transparently to the solver.
|
|
if (auto *existential = fromType->getAs<ExistentialType>()) {
|
|
if (auto *PCT = existential->getConstraintType()
|
|
->getAs<ProtocolCompositionType>()) {
|
|
if (PCT->withoutMarkerProtocols()->isEqual(toType)) {
|
|
return coerceSuperclass(expr, toType);
|
|
}
|
|
}
|
|
}
|
|
|
|
ABORT([&](auto &out) {
|
|
out << "Unhandled coercion:\n";
|
|
fromType->dump(out);
|
|
toType->dump(out);
|
|
});
|
|
}
|
|
|
|
static bool isSelfRefInInitializer(Expr *baseExpr,
|
|
DeclContext *useDC) {
|
|
auto *CD = dyn_cast<ConstructorDecl>(useDC);
|
|
return CD && baseExpr->isSelfExprOf(CD);
|
|
}
|
|
|
|
/// Detect whether an assignment to \c baseExpr.member in the given
|
|
/// decl context can potentially be initialization of a property wrapper.
|
|
static bool isPotentialPropertyWrapperInit(Expr *baseExpr,
|
|
ValueDecl *member,
|
|
DeclContext *UseDC) {
|
|
// Member is not a wrapped property
|
|
auto *VD = dyn_cast<VarDecl>(member);
|
|
if (!(VD && VD->hasAttachedPropertyWrapper()))
|
|
return false;
|
|
|
|
// Assignment to a wrapped property can only be re-written to
|
|
// initialization in an init.
|
|
return isSelfRefInInitializer(baseExpr, UseDC);
|
|
}
|
|
|
|
/// Detect whether an assignment to \c baseExpr.member in the given
|
|
/// decl context can potentially be initialization via an init accessor.
|
|
static bool isPotentialInitViaInitAccessor(Expr *baseExpr,
|
|
ValueDecl *member,
|
|
DeclContext *useDC) {
|
|
auto *VD = dyn_cast<VarDecl>(member);
|
|
if (!(VD && VD->hasInitAccessor()))
|
|
return false;
|
|
|
|
return isSelfRefInInitializer(baseExpr, useDC);
|
|
}
|
|
|
|
/// Adjust the given type to become the self type when referring to
|
|
/// the given member.
|
|
static Type adjustSelfTypeForMember(Expr *baseExpr,
|
|
Type baseTy, ValueDecl *member,
|
|
DeclContext *UseDC) {
|
|
assert(!baseTy->is<LValueType>());
|
|
|
|
auto inOutTy = baseTy->getAs<InOutType>();
|
|
if (!inOutTy)
|
|
return baseTy;
|
|
|
|
auto baseObjectTy = inOutTy->getObjectType();
|
|
|
|
if (isa<ConstructorDecl>(member))
|
|
return baseObjectTy;
|
|
|
|
if (auto func = dyn_cast<FuncDecl>(member)) {
|
|
// If 'self' is an inout type, turn the base type into an lvalue
|
|
// type with the same qualifiers.
|
|
if (func->isMutating())
|
|
return baseTy;
|
|
|
|
// Otherwise, return the rvalue type.
|
|
return baseObjectTy;
|
|
}
|
|
|
|
// If the base of the access is mutable, then we may be invoking a getter or
|
|
// setter that requires the base to be mutable.
|
|
auto *SD = cast<AbstractStorageDecl>(member);
|
|
bool isSettableFromHere =
|
|
SD->isSettable(UseDC) && SD->isSetterAccessibleFrom(UseDC);
|
|
|
|
// If neither the property's getter nor its setter are mutating,
|
|
// the base can be an rvalue unless the assignment is potentially
|
|
// initializing a property wrapper or using init accessor. If the
|
|
// assignment can be re-written to property wrapper or init accessor
|
|
// initialization, the base type should be an lvalue.
|
|
if (!SD->isGetterMutating() &&
|
|
(!isSettableFromHere || !SD->isSetterMutating()) &&
|
|
!isPotentialPropertyWrapperInit(baseExpr, member, UseDC) &&
|
|
!isPotentialInitViaInitAccessor(baseExpr, member, UseDC))
|
|
return baseObjectTy;
|
|
|
|
if (isa<SubscriptDecl>(member))
|
|
return baseTy;
|
|
|
|
return LValueType::get(baseObjectTy);
|
|
}
|
|
|
|
Expr *
|
|
ExprRewriter::coerceSelfArgumentToType(Expr *expr,
|
|
Type baseTy, ValueDecl *member,
|
|
ConstraintLocatorBuilder locator) {
|
|
Type toType = adjustSelfTypeForMember(expr, baseTy, member, dc);
|
|
return coerceToType(expr, toType, locator);
|
|
}
|
|
|
|
Expr *ExprRewriter::convertLiteralInPlace(
|
|
LiteralExpr *literal, Type type, ProtocolDecl *protocol,
|
|
Identifier literalType, DeclName literalFuncName,
|
|
ProtocolDecl *builtinProtocol, DeclName builtinLiteralFuncName,
|
|
Diag<> brokenProtocolDiag, Diag<> brokenBuiltinProtocolDiag) {
|
|
// Check whether this literal type conforms to the builtin protocol. If so,
|
|
// initialize via the builtin protocol.
|
|
if (builtinProtocol) {
|
|
auto builtinConformance = checkConformance(type, builtinProtocol);
|
|
if (builtinConformance) {
|
|
// Find the witness that we'll use to initialize the type via a builtin
|
|
// literal.
|
|
auto witness = builtinConformance.getWitnessByName(
|
|
builtinLiteralFuncName);
|
|
if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
|
|
return nullptr;
|
|
|
|
// Form a reference to the builtin conversion function.
|
|
|
|
// Set the builtin initializer.
|
|
dyn_cast<BuiltinLiteralExpr>(literal)->setBuiltinInitializer(witness);
|
|
|
|
// The literal expression has this type.
|
|
cs.setType(literal, type);
|
|
|
|
return literal;
|
|
}
|
|
}
|
|
|
|
// This literal type must conform to the (non-builtin) protocol.
|
|
assert(protocol && "requirements should have stopped recursion");
|
|
auto conformance = checkConformance(type, protocol);
|
|
assert(conformance && "must conform to literal protocol");
|
|
|
|
// Dig out the literal type and perform a builtin literal conversion to it.
|
|
if (!literalType.empty()) {
|
|
// Extract the literal type.
|
|
Type builtinLiteralType =
|
|
conformance.getTypeWitnessByName(literalType);
|
|
if (builtinLiteralType->hasError())
|
|
return nullptr;
|
|
|
|
// Perform the builtin conversion.
|
|
if (!convertLiteralInPlace(literal, builtinLiteralType, nullptr,
|
|
Identifier(), DeclName(), builtinProtocol,
|
|
builtinLiteralFuncName, brokenProtocolDiag,
|
|
brokenBuiltinProtocolDiag))
|
|
return nullptr;
|
|
}
|
|
|
|
// Find the witness that we'll use to initialize the literal value.
|
|
auto witness =
|
|
conformance.getWitnessByName(literalFuncName);
|
|
if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
|
|
return nullptr;
|
|
|
|
// Set the initializer.
|
|
literal->setInitializer(witness);
|
|
|
|
// The literal expression has this type.
|
|
cs.setType(literal, type);
|
|
|
|
return literal;
|
|
}
|
|
|
|
// Returns true if the given method and method type are a valid
|
|
// `@dynamicCallable` required `func dynamicallyCall` method.
|
|
static bool isValidDynamicCallableMethod(FuncDecl *method,
|
|
AnyFunctionType *methodType) {
|
|
auto &ctx = method->getASTContext();
|
|
if (method->getBaseIdentifier() != ctx.Id_dynamicallyCall)
|
|
return false;
|
|
if (methodType->getParams().size() != 1)
|
|
return false;
|
|
auto argumentLabel = methodType->getParams()[0].getLabel();
|
|
if (argumentLabel != ctx.Id_withArguments &&
|
|
argumentLabel != ctx.Id_withKeywordArguments)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// Build a reference to a `callAsFunction` method.
|
|
static Expr *buildCallAsFunctionMethodRef(
|
|
ExprRewriter &rewriter, ApplyExpr *apply, SelectedOverload selected,
|
|
ConstraintLocator *calleeLoc) {
|
|
assert(calleeLoc->isLastElement<LocatorPathElt::ImplicitCallAsFunction>());
|
|
assert(cast<FuncDecl>(selected.choice.getDecl())->isCallAsFunctionMethod());
|
|
|
|
// Create direct reference to `callAsFunction` method.
|
|
auto *fn = apply->getFn();
|
|
auto *args = apply->getArgs();
|
|
|
|
// HACK: Temporarily push the fn expr onto the expr stack to make sure we
|
|
// don't try to prematurely close an existential when applying the curried
|
|
// member ref. This can be removed once existential opening is refactored not
|
|
// to rely on the shape of the AST prior to rewriting.
|
|
rewriter.ExprStack.push_back(fn);
|
|
SWIFT_DEFER {
|
|
rewriter.ExprStack.pop_back();
|
|
};
|
|
|
|
auto *declRef = rewriter.buildMemberRef(
|
|
fn, /*dotLoc*/ SourceLoc(), selected, DeclNameLoc(args->getStartLoc()),
|
|
calleeLoc, calleeLoc, /*implicit*/ true, AccessSemantics::Ordinary);
|
|
if (!declRef)
|
|
return nullptr;
|
|
declRef->setImplicit(apply->isImplicit());
|
|
return declRef;
|
|
}
|
|
|
|
// Resolve `@dynamicCallable` applications.
|
|
std::pair<Expr *, ArgumentList *> ExprRewriter::buildDynamicCallable(
|
|
ApplyExpr *apply, SelectedOverload selected, FuncDecl *method,
|
|
AnyFunctionType *methodType, ConstraintLocatorBuilder loc) {
|
|
auto *fn = apply->getFn();
|
|
|
|
auto *args = apply->getArgs();
|
|
|
|
// Get resolved `dynamicallyCall` method and verify it.
|
|
assert(isValidDynamicCallableMethod(method, methodType));
|
|
auto params = methodType->getParams();
|
|
auto argumentType = params[0].getParameterType();
|
|
|
|
// Determine which method was resolved: a `withArguments` method or a
|
|
// `withKeywordArguments` method.
|
|
auto argumentLabel = methodType->getParams()[0].getLabel();
|
|
bool useKwargsMethod = argumentLabel == ctx.Id_withKeywordArguments;
|
|
|
|
// HACK: Temporarily push the fn expr onto the expr stack to make sure we
|
|
// don't try to prematurely close an existential when applying the curried
|
|
// member ref. This can be removed once existential opening is refactored not
|
|
// to rely on the shape of the AST prior to rewriting.
|
|
ExprStack.push_back(fn);
|
|
SWIFT_DEFER {
|
|
ExprStack.pop_back();
|
|
};
|
|
|
|
// Construct expression referencing the `dynamicallyCall` method.
|
|
auto member = buildMemberRef(fn, SourceLoc(), selected,
|
|
DeclNameLoc(), loc, loc,
|
|
/*implicit=*/true, AccessSemantics::Ordinary);
|
|
|
|
// Construct argument to the method (either an array or dictionary
|
|
// expression).
|
|
Expr *argExpr = nullptr;
|
|
if (!useKwargsMethod) {
|
|
argExpr = ArrayExpr::create(ctx, SourceLoc(), args->getArgExprs(), {},
|
|
SourceLoc());
|
|
cs.setType(argExpr, argumentType);
|
|
finishArrayExpr(cast<ArrayExpr>(argExpr));
|
|
} else {
|
|
auto dictLitProto =
|
|
ctx.getProtocol(KnownProtocolKind::ExpressibleByDictionaryLiteral);
|
|
auto conformance = checkConformance(argumentType, dictLitProto);
|
|
auto keyType = conformance.getTypeWitnessByName(ctx.Id_Key);
|
|
auto valueType = conformance.getTypeWitnessByName(ctx.Id_Value);
|
|
SmallVector<Identifier, 4> names;
|
|
SmallVector<Expr *, 4> dictElements;
|
|
for (auto arg : *args) {
|
|
Expr *labelExpr =
|
|
new (ctx) StringLiteralExpr(arg.getLabel().get(), arg.getLabelLoc(),
|
|
/*Implicit*/ true);
|
|
cs.setType(labelExpr, keyType);
|
|
handleStringLiteralExpr(cast<LiteralExpr>(labelExpr));
|
|
|
|
Expr *valueExpr = coerceToType(arg.getExpr(), valueType, loc);
|
|
assert(valueExpr && "Failed to coerce?");
|
|
Expr *pair = TupleExpr::createImplicit(ctx, {labelExpr, valueExpr}, {});
|
|
auto eltTypes = { TupleTypeElt(keyType), TupleTypeElt(valueType) };
|
|
cs.setType(pair, TupleType::get(eltTypes, ctx));
|
|
dictElements.push_back(pair);
|
|
}
|
|
argExpr = DictionaryExpr::create(ctx, SourceLoc(), dictElements, {},
|
|
SourceLoc());
|
|
cs.setType(argExpr, argumentType);
|
|
finishDictionaryExpr(cast<DictionaryExpr>(argExpr));
|
|
}
|
|
argExpr->setImplicit();
|
|
|
|
auto *argList = ArgumentList::forImplicitSingle(ctx, argumentLabel, argExpr);
|
|
return std::make_pair(member, argList);
|
|
}
|
|
|
|
Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
|
|
ConstraintLocatorBuilder locator,
|
|
ConstraintLocatorBuilder calleeLocator) {
|
|
auto args = apply->getArgs();
|
|
auto *fn = apply->getFn();
|
|
|
|
auto finishApplyOfDeclWithSpecialTypeCheckingSemantics
|
|
= [&](ApplyExpr *apply,
|
|
ConcreteDeclRef declRef,
|
|
Type openedType) -> Expr* {
|
|
switch (TypeChecker::getDeclTypeCheckingSemantics(declRef.getDecl())) {
|
|
case DeclTypeCheckingSemantics::TypeOf: {
|
|
// Resolve into a DynamicTypeExpr.
|
|
auto args = apply->getArgs();
|
|
|
|
auto appliedWrappers = solution.getAppliedPropertyWrappers(
|
|
calleeLocator.getAnchor());
|
|
auto fnType = cs.getType(fn)->getAs<FunctionType>();
|
|
args = coerceCallArguments(
|
|
args, fnType, declRef, apply,
|
|
locator.withPathElement(ConstraintLocator::ApplyArgument),
|
|
appliedWrappers);
|
|
if (!args)
|
|
return nullptr;
|
|
|
|
auto replacement = new (ctx)
|
|
DynamicTypeExpr(apply->getFn()->getLoc(), args->getStartLoc(),
|
|
args->getExpr(0), args->getEndLoc(), Type());
|
|
cs.setType(replacement, simplifyType(openedType));
|
|
return replacement;
|
|
}
|
|
|
|
case DeclTypeCheckingSemantics::WithoutActuallyEscaping: {
|
|
// Resolve into a MakeTemporarilyEscapableExpr.
|
|
auto *args = apply->getArgs();
|
|
assert(args->size() == 2 && "should have two arguments");
|
|
auto *nonescaping = args->getExpr(0);
|
|
auto *body = args->getExpr(1);
|
|
auto bodyTy = cs.getType(body)->getWithoutSpecifierType();
|
|
auto bodyFnTy = bodyTy->castTo<FunctionType>();
|
|
auto resultType = bodyFnTy->getResult();
|
|
|
|
// The body is immediately called, so is obviously noescape.
|
|
// Coerce the argument function to be escaping even if it happens to
|
|
// be nonescaping, since we need the dynamic state of the escaping
|
|
// closure to do the dynamic noescape check.
|
|
auto bodyArgFnTy = bodyFnTy->getParams()[0].getPlainType()
|
|
->castTo<FunctionType>();
|
|
|
|
bodyArgFnTy = cast<FunctionType>(
|
|
bodyArgFnTy->withExtInfo(bodyArgFnTy->getExtInfo().withNoEscape(false)));
|
|
bodyFnTy = cast<FunctionType>(
|
|
FunctionType::get(bodyFnTy->getParams()[0].withType(bodyArgFnTy),
|
|
bodyFnTy->getResult())
|
|
->withExtInfo(bodyFnTy->getExtInfo().withNoEscape()));
|
|
body = coerceToType(body, bodyFnTy, locator);
|
|
assert(body && "can't make nonescaping?!");
|
|
|
|
auto escapable = new (ctx)
|
|
OpaqueValueExpr(apply->getFn()->getSourceRange(), Type());
|
|
cs.setType(escapable, bodyArgFnTy);
|
|
|
|
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {escapable});
|
|
auto callSubExpr = CallExpr::createImplicit(ctx, body, argList);
|
|
cs.cacheSubExprTypes(callSubExpr);
|
|
cs.setType(callSubExpr, resultType);
|
|
|
|
auto replacement = new (ctx)
|
|
MakeTemporarilyEscapableExpr(apply->getFn()->getLoc(),
|
|
apply->getArgs()->getStartLoc(),
|
|
nonescaping,
|
|
callSubExpr,
|
|
apply->getArgs()->getEndLoc(),
|
|
escapable,
|
|
apply);
|
|
cs.setType(replacement, resultType);
|
|
return replacement;
|
|
}
|
|
|
|
case DeclTypeCheckingSemantics::OpenExistential: {
|
|
// Resolve into an OpenExistentialExpr.
|
|
auto *args = apply->getArgs();
|
|
assert(args->size() == 2 && "should have two arguments");
|
|
|
|
auto *existential = cs.coerceToRValue(args->getExpr(0));
|
|
auto *body = cs.coerceToRValue(args->getExpr(1));
|
|
|
|
auto bodyFnTy = cs.getType(body)->castTo<FunctionType>();
|
|
auto openedTy = getBaseType(bodyFnTy, /*wantsRValue*/ false);
|
|
auto resultTy = bodyFnTy->getResult();
|
|
|
|
// The body is immediately called, so is obviously noescape.
|
|
bodyFnTy = cast<FunctionType>(
|
|
bodyFnTy->withExtInfo(bodyFnTy->getExtInfo().withNoEscape()));
|
|
body = coerceToType(body, bodyFnTy, locator);
|
|
assert(body && "can't make nonescaping?!");
|
|
|
|
auto openedInstanceTy = openedTy;
|
|
auto existentialInstanceTy = cs.getType(existential);
|
|
if (auto metaTy = openedTy->getAs<MetatypeType>()) {
|
|
openedInstanceTy = metaTy->getInstanceType();
|
|
existentialInstanceTy = existentialInstanceTy
|
|
->castTo<ExistentialMetatypeType>()
|
|
->getExistentialInstanceType();
|
|
}
|
|
assert(openedInstanceTy->castTo<ExistentialArchetypeType>()
|
|
->getExistentialType()
|
|
->isEqual(existentialInstanceTy));
|
|
|
|
auto opaqueValue =
|
|
new (ctx) OpaqueValueExpr(apply->getSourceRange(), openedTy);
|
|
cs.setType(opaqueValue, openedTy);
|
|
|
|
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {opaqueValue});
|
|
auto callSubExpr = CallExpr::createImplicit(ctx, body, argList);
|
|
cs.cacheSubExprTypes(callSubExpr);
|
|
cs.setType(callSubExpr, resultTy);
|
|
|
|
auto replacement = new (ctx)
|
|
OpenExistentialExpr(existential, opaqueValue, callSubExpr,
|
|
resultTy);
|
|
cs.setType(replacement, resultTy);
|
|
return replacement;
|
|
}
|
|
|
|
case DeclTypeCheckingSemantics::Normal:
|
|
return nullptr;
|
|
}
|
|
|
|
llvm_unreachable("Unhandled DeclTypeCheckingSemantics in switch.");
|
|
};
|
|
|
|
// Resolve the callee for the application if we have one.
|
|
ConcreteDeclRef callee;
|
|
auto *calleeLoc = cs.getConstraintLocator(calleeLocator);
|
|
auto overload = solution.getOverloadChoiceIfAvailable(calleeLoc);
|
|
if (overload) {
|
|
auto *decl = overload->choice.getDeclOrNull();
|
|
callee = resolveConcreteDeclRef(decl, calleeLoc);
|
|
}
|
|
|
|
// Make sure we have a function type that is callable. This helps ensure
|
|
// Type::mayBeCallable stays up-to-date.
|
|
auto fnRValueTy = cs.getType(fn)->getRValueType();
|
|
assert(fnRValueTy->mayBeCallable(dc));
|
|
|
|
// If this is an implicit call to a `callAsFunction` method, build the
|
|
// appropriate member reference.
|
|
if (fnRValueTy->isCallAsFunctionType(dc)) {
|
|
fn = buildCallAsFunctionMethodRef(*this, apply, *overload, calleeLoc);
|
|
if (!fn)
|
|
return nullptr;
|
|
}
|
|
|
|
// Resolve a `@dynamicCallable` application.
|
|
auto applyFunctionLoc =
|
|
locator.withPathElement(ConstraintLocator::ApplyFunction);
|
|
if (auto selected = solution.getOverloadChoiceIfAvailable(
|
|
cs.getConstraintLocator(applyFunctionLoc))) {
|
|
auto *method = dyn_cast<FuncDecl>(selected->choice.getDecl());
|
|
auto methodType =
|
|
simplifyType(selected->adjustedOpenedType)->getAs<AnyFunctionType>();
|
|
if (method && methodType) {
|
|
// Handle a call to a @dynamicCallable method.
|
|
if (isValidDynamicCallableMethod(method, methodType))
|
|
std::tie(fn, args) = buildDynamicCallable(apply, *selected, method,
|
|
methodType, applyFunctionLoc);
|
|
}
|
|
}
|
|
|
|
// The function is always an rvalue.
|
|
fn = cs.coerceToRValue(fn);
|
|
|
|
// Resolve applications of decls with special semantics.
|
|
if (auto declRef =
|
|
dyn_cast<DeclRefExpr>(getSemanticExprForDeclOrMemberRef(fn))) {
|
|
if (auto special =
|
|
finishApplyOfDeclWithSpecialTypeCheckingSemantics(apply,
|
|
declRef->getDeclRef(),
|
|
openedType)) {
|
|
return special;
|
|
}
|
|
}
|
|
|
|
// If we're applying a function that resulted from a covariant
|
|
// function conversion, strip off that conversion.
|
|
// FIXME: It would be nicer if we could build the ASTs properly in the
|
|
// first shot.
|
|
Type covariantResultType;
|
|
if (auto covariant = dyn_cast<CovariantFunctionConversionExpr>(fn)) {
|
|
// Strip off one layer of application from the covariant result.
|
|
covariantResultType
|
|
= cs.getType(covariant)->castTo<AnyFunctionType>()->getResult();
|
|
|
|
// Use the subexpression as the function.
|
|
fn = covariant->getSubExpr();
|
|
}
|
|
|
|
// An immediate application of a closure literal is always noescape.
|
|
if (isClosureLiteralExpr(fn)) {
|
|
if (auto fnTy = cs.getType(fn)->getAs<FunctionType>()) {
|
|
fnTy = cast<FunctionType>(
|
|
fnTy->withExtInfo(fnTy->getExtInfo().withNoEscape()));
|
|
fn = coerceToType(fn, fnTy, locator);
|
|
}
|
|
}
|
|
|
|
apply->setFn(fn);
|
|
|
|
// For function application, convert the argument to the input type of
|
|
// the function.
|
|
if (auto fnType = cs.getType(fn)->getAs<FunctionType>()) {
|
|
auto appliedWrappers = solution.getAppliedPropertyWrappers(
|
|
calleeLocator.getAnchor());
|
|
|
|
args = coerceCallArguments(
|
|
args, fnType, callee, apply,
|
|
locator.withPathElement(ConstraintLocator::ApplyArgument),
|
|
appliedWrappers);
|
|
if (!args)
|
|
return nullptr;
|
|
|
|
apply->setArgs(args);
|
|
cs.setType(apply, fnType->getResult());
|
|
|
|
// If this is a call to a distributed method thunk,
|
|
// let's mark the call as implicitly throwing.
|
|
if (isDistributedThunk(callee, apply->getFn())) {
|
|
apply->setImplicitlyThrows(true);
|
|
}
|
|
|
|
solution.setExprTypes(apply);
|
|
Expr *result = TypeChecker::substituteInputSugarTypeForResult(apply);
|
|
cs.cacheExprTypes(result);
|
|
|
|
// If we have a covariant result type, perform the conversion now.
|
|
if (covariantResultType) {
|
|
if (covariantResultType->is<FunctionType>())
|
|
result = cs.cacheType(new (ctx) CovariantFunctionConversionExpr(
|
|
result, covariantResultType));
|
|
else
|
|
result = cs.cacheType(new (ctx) CovariantReturnConversionExpr(
|
|
result, covariantResultType));
|
|
}
|
|
|
|
// Try closing existentials, if there are any.
|
|
closeExistentials(result, locator);
|
|
|
|
// We may also need to force the result for an IUO. We don't apply this on
|
|
// SelfApplyExprs, as the force unwraps should be inserted at the result of
|
|
// main application, not on the curried member reference.
|
|
if (!isa<SelfApplyExpr>(apply)) {
|
|
result = forceUnwrapIfExpected(result, calleeLocator,
|
|
IUOReferenceKind::ReturnValue);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// FIXME: Handle unwrapping everywhere else.
|
|
|
|
// We have a type constructor.
|
|
auto metaTy = cs.getType(fn)->castTo<AnyMetatypeType>();
|
|
auto ty = metaTy->getInstanceType();
|
|
|
|
// If we're "constructing" a tuple type, it's simply a conversion.
|
|
if (auto tupleTy = ty->getAs<TupleType>()) {
|
|
auto *packed = apply->getArgs()->packIntoImplicitTupleOrParen(
|
|
ctx, [&](Expr *E) { return cs.getType(E); });
|
|
cs.cacheType(packed);
|
|
auto *result = coerceToType(packed, tupleTy, cs.getConstraintLocator(packed));
|
|
// Resetting the types of tuple is necessary because
|
|
// `packIntoImplicitTupleOrParen` sets types in AST
|
|
// where `coerceToType` only updates constraint system
|
|
// cache. This creates a mismatch that would not be
|
|
// corrected by `setExprTypes` if this tuple is used
|
|
// as an argument that is wrapped in an autoclosure.
|
|
solution.setExprTypes(result);
|
|
return result;
|
|
}
|
|
|
|
// We're constructing a value of nominal type. Look for the constructor or
|
|
// enum element to use.
|
|
auto *ctorLocator =
|
|
cs.getConstraintLocator(locator, {ConstraintLocator::ApplyFunction,
|
|
ConstraintLocator::ConstructorMember});
|
|
auto selected = solution.getOverloadChoice(ctorLocator);
|
|
|
|
assert(ty->getNominalOrBoundGenericNominal() || ty->is<DynamicSelfType>() ||
|
|
ty->isExistentialType() || ty->is<ArchetypeType>());
|
|
|
|
// Consider the constructor decl reference expr 'implicit', but the
|
|
// constructor call expr itself has the apply's 'implicitness'.
|
|
Expr *declRef = buildMemberRef(
|
|
fn, /*dotLoc=*/SourceLoc(), selected, DeclNameLoc(fn->getEndLoc()),
|
|
locator, ctorLocator, /*implicit=*/true, AccessSemantics::Ordinary);
|
|
if (!declRef)
|
|
return nullptr;
|
|
|
|
if (!isa<AutoClosureExpr>(declRef))
|
|
declRef->setImplicit(apply->isImplicit());
|
|
|
|
apply->setFn(declRef);
|
|
|
|
// Tail-recur to actually call the constructor.
|
|
auto *ctorCall = finishApply(apply, openedType, locator, ctorLocator);
|
|
|
|
// Check whether this is a situation like `T(...) { ... }` where `T` is
|
|
// a callable type and trailing closure(s) are associated with implicit
|
|
// `.callAsFunction` instead of constructor.
|
|
{
|
|
auto callAsFunction =
|
|
solution.ImplicitCallAsFunctionRoots.find(ctorLocator);
|
|
if (callAsFunction != solution.ImplicitCallAsFunctionRoots.end()) {
|
|
auto *dotExpr = callAsFunction->second;
|
|
auto resultTy = solution.getResolvedType(dotExpr);
|
|
|
|
auto *implicitCall = CallExpr::createImplicit(
|
|
ctx, ctorCall,
|
|
solution.getArgumentList(cs.getConstraintLocator(
|
|
dotExpr, ConstraintLocator::ApplyArgument)));
|
|
|
|
implicitCall->setType(resultTy);
|
|
cs.cacheType(implicitCall);
|
|
|
|
auto *memberCalleeLoc =
|
|
cs.getConstraintLocator(dotExpr,
|
|
{ConstraintLocator::ApplyFunction,
|
|
ConstraintLocator::ImplicitCallAsFunction},
|
|
/*summaryFlags=*/0);
|
|
|
|
return finishApply(implicitCall, resultTy, cs.getConstraintLocator(dotExpr),
|
|
memberCalleeLoc);
|
|
}
|
|
}
|
|
|
|
return ctorCall;
|
|
}
|
|
|
|
bool ExprRewriter::isDistributedThunk(ConcreteDeclRef ref, Expr *context) {
|
|
auto *FD = dyn_cast_or_null<AbstractFunctionDecl>(ref.getDecl());
|
|
|
|
if (!(FD && FD->isInstanceMember() && FD->isDistributed()))
|
|
return false;
|
|
|
|
if (!isa<SelfApplyExpr>(context))
|
|
return false;
|
|
|
|
auto *actor = getReferencedParamOrCapture(
|
|
cast<SelfApplyExpr>(context)->getBase(),
|
|
[&](OpaqueValueExpr *opaqueValue) -> Expr * {
|
|
for (const auto &existential : OpenedExistentials) {
|
|
if (existential.OpaqueValue == opaqueValue)
|
|
return existential.ExistentialValue;
|
|
}
|
|
return nullptr;
|
|
},
|
|
[]() -> VarDecl * {
|
|
// FIXME: Need to communicate this.
|
|
return nullptr;
|
|
});
|
|
|
|
if (!actor)
|
|
return false;
|
|
|
|
// If this is a method reference on an potentially isolated
|
|
// actor then it cannot be a remote thunk.
|
|
bool isPotentiallyIsolated = isPotentiallyIsolatedActor(
|
|
actor, [&](ParamDecl *P) {
|
|
return P->isIsolated() || solution.isolatedParams.count(P);
|
|
});
|
|
|
|
// Adjust the declaration context to the innermost context that is neither
|
|
// a local function nor a closure, so that the actor reference is checked
|
|
auto referenceDC = dc;
|
|
while (true) {
|
|
switch (referenceDC->getContextKind()) {
|
|
case DeclContextKind::AbstractClosureExpr:
|
|
case DeclContextKind::SerializedAbstractClosure:
|
|
case DeclContextKind::Initializer:
|
|
referenceDC = referenceDC->getParent();
|
|
continue;
|
|
|
|
case DeclContextKind::AbstractFunctionDecl:
|
|
case DeclContextKind::GenericTypeDecl:
|
|
case DeclContextKind::SubscriptDecl:
|
|
if (auto value = dyn_cast<ValueDecl>(referenceDC->getAsDecl())) {
|
|
if (value->isLocalCapture()) {
|
|
referenceDC = referenceDC->getParent();
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DeclContextKind::EnumElementDecl:
|
|
case DeclContextKind::ExtensionDecl:
|
|
case DeclContextKind::FileUnit:
|
|
case DeclContextKind::Package:
|
|
case DeclContextKind::Module:
|
|
case DeclContextKind::TopLevelCodeDecl:
|
|
case DeclContextKind::SerializedTopLevelCodeDecl:
|
|
case DeclContextKind::MacroDecl:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Create a simple actor reference, assuming that we might be in a
|
|
// non-isolated context but knowing whether it's potentially isolated.
|
|
// We only care about the "distributed" flag.
|
|
ReferencedActor actorRef = ReferencedActor(
|
|
actor, isPotentiallyIsolated, ReferencedActor::NonIsolatedContext);
|
|
auto refResult = ActorReferenceResult::forReference(
|
|
ref, context->getLoc(), referenceDC, std::nullopt, actorRef);
|
|
switch (refResult) {
|
|
case ActorReferenceResult::ExitsActorToNonisolated:
|
|
case ActorReferenceResult::SameConcurrencyDomain:
|
|
return false;
|
|
|
|
case ActorReferenceResult::EntersActor:
|
|
return refResult.options.contains(ActorReferenceResult::Flags::Distributed);
|
|
}
|
|
}
|
|
|
|
// Return the precedence-yielding parent of 'expr', along with the index of
|
|
// 'expr' as the child of that parent. The precedence-yielding parent is the
|
|
// nearest ancestor of 'expr' which imposes a minimum precedence on 'expr'.
|
|
static std::pair<Expr *, unsigned> getPrecedenceParentAndIndex(
|
|
Expr *expr, llvm::function_ref<Expr *(const Expr *)> getParent) {
|
|
auto *parent = getParent(expr);
|
|
if (!parent)
|
|
return { nullptr, 0 };
|
|
|
|
// Look through an unresolved chain wrappers, try, and await exprs, as they
|
|
// have no effect on precedence; they will associate the same with any parent
|
|
// operator as their sub-expression would.
|
|
while (isa<UnresolvedMemberChainResultExpr>(parent) ||
|
|
isa<AnyTryExpr>(parent) || isa<AwaitExpr>(parent) ||
|
|
isa<UnsafeExpr>(parent)) {
|
|
expr = parent;
|
|
parent = getParent(parent);
|
|
if (!parent)
|
|
return { nullptr, 0 };
|
|
}
|
|
|
|
// Handle all cases where the answer isn't just going to be { parent, 0 }.
|
|
if (auto tuple = dyn_cast<TupleExpr>(parent)) {
|
|
// Get index of expression in tuple.
|
|
auto tupleElems = tuple->getElements();
|
|
auto elemIt = std::find(tupleElems.begin(), tupleElems.end(), expr);
|
|
assert(elemIt != tupleElems.end() && "expr not found in parent TupleExpr");
|
|
unsigned index = elemIt - tupleElems.begin();
|
|
return { tuple, index };
|
|
} else if (auto *BE = dyn_cast<BinaryExpr>(parent)) {
|
|
if (BE->getLHS() == expr)
|
|
return {parent, 0};
|
|
if (BE->getRHS() == expr)
|
|
return {parent, 1};
|
|
} else if (auto *ternary = dyn_cast<TernaryExpr>(parent)) {
|
|
unsigned index;
|
|
if (expr == ternary->getCondExpr()) {
|
|
index = 0;
|
|
} else if (expr == ternary->getThenExpr()) {
|
|
index = 1;
|
|
} else if (expr == ternary->getElseExpr()) {
|
|
index = 2;
|
|
} else {
|
|
llvm_unreachable("expr not found in parent TernaryExpr");
|
|
}
|
|
return { ternary, index };
|
|
} else if (auto assignExpr = dyn_cast<AssignExpr>(parent)) {
|
|
unsigned index;
|
|
if (expr == assignExpr->getSrc()) {
|
|
index = 0;
|
|
} else if (expr == assignExpr->getDest()) {
|
|
index = 1;
|
|
} else {
|
|
llvm_unreachable("expr not found in parent AssignExpr");
|
|
}
|
|
return { assignExpr, index };
|
|
}
|
|
|
|
return { parent, 0 };
|
|
}
|
|
|
|
/// Return true if, when replacing "<expr>" with "<expr> op <something>",
|
|
/// parentheses must be added around "<expr>" to allow the new operator
|
|
/// to bind correctly.
|
|
bool swift::exprNeedsParensInsideFollowingOperator(
|
|
DeclContext *DC, Expr *expr,
|
|
PrecedenceGroupDecl *followingPG) {
|
|
if (expr->isInfixOperator()) {
|
|
auto exprPG = TypeChecker::lookupPrecedenceGroupForInfixOperator(
|
|
DC, expr, /*diagnose=*/false);
|
|
if (!exprPG) return true;
|
|
|
|
return DC->getASTContext().associateInfixOperators(exprPG, followingPG)
|
|
!= Associativity::Left;
|
|
}
|
|
|
|
// We want to parenthesize a 'try?' on the LHS, but we don't care about
|
|
// capturing the new operator inside a 'try' or 'try!'.
|
|
if (isa<OptionalTryExpr>(expr))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Return true if, when replacing "<expr>" with "<expr> op <something>"
|
|
/// within the given root expression, parentheses must be added around
|
|
/// the new operator to prevent it from binding incorrectly in the
|
|
/// surrounding context.
|
|
bool swift::exprNeedsParensOutsideFollowingOperator(
|
|
DeclContext *DC, Expr *expr, PrecedenceGroupDecl *followingPG,
|
|
llvm::function_ref<Expr *(const Expr *)> getParent) {
|
|
Expr *parent;
|
|
unsigned index;
|
|
std::tie(parent, index) = getPrecedenceParentAndIndex(expr, getParent);
|
|
if (!parent)
|
|
return false;
|
|
|
|
// If this is a call argument, no parens are needed.
|
|
if (auto *args = parent->getArgs()) {
|
|
if (!args->isImplicit() && args->findArgumentExpr(expr))
|
|
return false;
|
|
}
|
|
|
|
// If this is a key-path, no parens needed if it's an arg of one of the
|
|
// components.
|
|
if (auto *KP = dyn_cast<KeyPathExpr>(parent)) {
|
|
if (KP->findComponentWithSubscriptArg(expr))
|
|
return false;
|
|
}
|
|
|
|
if (isa<ParenExpr>(parent) || isa<TupleExpr>(parent)) {
|
|
if (!parent->isImplicit())
|
|
return false;
|
|
}
|
|
|
|
if (isa<ClosureExpr>(parent) || isa<CollectionExpr>(parent))
|
|
return false;
|
|
|
|
if (parent->isInfixOperator()) {
|
|
auto parentPG = TypeChecker::lookupPrecedenceGroupForInfixOperator(
|
|
DC, parent, /*diagnose=*/false);
|
|
if (!parentPG) return true;
|
|
|
|
// If the index is 0, this is on the LHS of the parent.
|
|
auto &Context = DC->getASTContext();
|
|
if (index == 0) {
|
|
return Context.associateInfixOperators(followingPG, parentPG)
|
|
!= Associativity::Left;
|
|
} else {
|
|
return Context.associateInfixOperators(parentPG, followingPG)
|
|
!= Associativity::Right;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool swift::exprNeedsParensBeforeAddingNilCoalescing(DeclContext *DC,
|
|
Expr *expr) {
|
|
auto &ctx = DC->getASTContext();
|
|
auto asPG = TypeChecker::lookupPrecedenceGroup(
|
|
DC, ctx.Id_NilCoalescingPrecedence, SourceLoc())
|
|
.getSingle();
|
|
if (!asPG)
|
|
return true;
|
|
return exprNeedsParensInsideFollowingOperator(DC, expr, asPG);
|
|
}
|
|
|
|
bool swift::exprNeedsParensAfterAddingNilCoalescing(
|
|
DeclContext *DC, Expr *expr,
|
|
llvm::function_ref<Expr *(const Expr *)> getParent) {
|
|
auto &ctx = DC->getASTContext();
|
|
auto asPG = TypeChecker::lookupPrecedenceGroup(
|
|
DC, ctx.Id_NilCoalescingPrecedence, SourceLoc())
|
|
.getSingle();
|
|
if (!asPG)
|
|
return true;
|
|
return exprNeedsParensOutsideFollowingOperator(DC, expr, asPG, getParent);
|
|
}
|
|
|
|
namespace {
|
|
class SetExprTypes : public ASTWalker {
|
|
const Solution &solution;
|
|
|
|
public:
|
|
explicit SetExprTypes(const Solution &solution)
|
|
: solution(solution) {}
|
|
|
|
MacroWalking getMacroWalkingBehavior() const override {
|
|
return MacroWalking::ArgumentsAndExpansion;
|
|
}
|
|
|
|
PostWalkResult<Expr *> walkToExprPost(Expr *expr) override {
|
|
auto &cs = solution.getConstraintSystem();
|
|
auto exprType = cs.getType(expr);
|
|
exprType = solution.simplifyType(exprType);
|
|
// assert((!expr->getType() || expr->getType()->isEqual(exprType)) &&
|
|
// "Mismatched types!");
|
|
assert(!exprType->getRecursiveProperties().isSolverAllocated() &&
|
|
"Should not write solver allocated type into expression!");
|
|
assert(!exprType->hasPlaceholder() &&
|
|
"Should not write type placeholders into expression!");
|
|
expr->setType(exprType);
|
|
|
|
if (auto kp = dyn_cast<KeyPathExpr>(expr)) {
|
|
for (auto i : indices(kp->getComponents())) {
|
|
Type componentType;
|
|
if (cs.hasType(kp, i)) {
|
|
componentType = solution.simplifyType(cs.getType(kp, i));
|
|
assert(
|
|
!componentType->getRecursiveProperties().isSolverAllocated() &&
|
|
"Should not write solver allocated type into key-path "
|
|
"component!");
|
|
assert(!componentType->hasPlaceholder() &&
|
|
"Should not write type placeholder into key-path component");
|
|
kp->getMutableComponents()[i].setComponentType(componentType);
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
};
|
|
|
|
class ExprWalker : public ASTWalker, public SyntacticElementTargetRewriter {
|
|
ExprRewriter &Rewriter;
|
|
|
|
public:
|
|
ExprWalker(ExprRewriter &Rewriter) : Rewriter(Rewriter) { }
|
|
|
|
Solution &getSolution() const override { return Rewriter.solution; }
|
|
DeclContext *&getCurrentDC() const override { return Rewriter.dc; }
|
|
|
|
void addLocalDeclToTypeCheck(Decl *D) override {
|
|
Rewriter.addLocalDeclToTypeCheck(D);
|
|
}
|
|
|
|
bool shouldWalkIntoPropertyWrapperPlaceholderValue() override {
|
|
// Property wrapper placeholder underlying values are filled in
|
|
// with already-type-checked expressions. Don't walk into them.
|
|
return false;
|
|
}
|
|
|
|
MacroWalking getMacroWalkingBehavior() const override {
|
|
return MacroWalking::Arguments;
|
|
}
|
|
|
|
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
|
|
// For closures, update the parameter types and check the body.
|
|
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
|
|
rewriteFunction(closure);
|
|
|
|
if (AnyFunctionRef(closure).hasExternalPropertyWrapperParameters()) {
|
|
auto *thunkTy = Rewriter.cs.getType(closure)->castTo<FunctionType>();
|
|
return Action::SkipNode(Rewriter.buildSingleCurryThunk(
|
|
closure, closure, thunkTy, thunkTy,
|
|
Rewriter.cs.getConstraintLocator(closure)));
|
|
}
|
|
|
|
return Action::SkipNode(closure);
|
|
}
|
|
|
|
if (auto *SVE = dyn_cast<SingleValueStmtExpr>(expr)) {
|
|
rewriteSingleValueStmtExpr(SVE);
|
|
return Action::SkipNode(SVE);
|
|
}
|
|
|
|
if (auto tap = dyn_cast_or_null<TapExpr>(expr)) {
|
|
rewriteTapExpr(tap);
|
|
return Action::SkipNode(tap);
|
|
}
|
|
|
|
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
|
|
// Rewrite captures.
|
|
for (const auto &capture : captureList->getCaptureList()) {
|
|
(void)rewriteTarget(SyntacticElementTarget(capture.PBD));
|
|
}
|
|
}
|
|
|
|
Rewriter.walkToExprPre(expr);
|
|
return Action::Continue(expr);
|
|
}
|
|
|
|
PostWalkResult<Expr *> walkToExprPost(Expr *expr) override {
|
|
auto *result = Rewriter.walkToExprPost(expr);
|
|
if (!result)
|
|
return Action::Stop();
|
|
|
|
return Action::Continue(result);
|
|
}
|
|
|
|
/// Ignore statements.
|
|
PreWalkResult<Stmt *> walkToStmtPre(Stmt *stmt) override {
|
|
return Action::SkipNode(stmt);
|
|
}
|
|
|
|
/// Ignore declarations.
|
|
PreWalkAction walkToDeclPre(Decl *decl) override {
|
|
return Action::SkipNode();
|
|
}
|
|
|
|
NullablePtr<Pattern>
|
|
rewritePattern(Pattern *pattern, DeclContext *DC);
|
|
|
|
/// Rewrite the target, producing a new target.
|
|
std::optional<SyntacticElementTarget>
|
|
rewriteTarget(SyntacticElementTarget target) override;
|
|
|
|
/// Rewrite the function for the given solution.
|
|
///
|
|
/// \returns true if an error occurred.
|
|
bool rewriteFunction(AnyFunctionRef fn) {
|
|
return Rewriter.cs.applySolution(fn, *this);
|
|
}
|
|
|
|
bool rewriteSingleValueStmtExpr(SingleValueStmtExpr *SVE) {
|
|
return Rewriter.cs.applySolutionToSingleValueStmt(SVE, *this);
|
|
}
|
|
|
|
void rewriteTapExpr(TapExpr *tap) {
|
|
// First, let's visit the tap expression itself
|
|
// and set all of the inferred types.
|
|
Rewriter.visitTapExpr(tap);
|
|
|
|
// Now, let's apply solution to the body
|
|
(void)Rewriter.cs.applySolutionToBody(tap, *this);
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
Expr *ConstraintSystem::coerceToRValue(Expr *expr) {
|
|
return TypeChecker::coerceToRValue(
|
|
getASTContext(), expr, [&](Expr *expr) { return getType(expr); },
|
|
[&](Expr *expr, Type type) { setType(expr, type); });
|
|
}
|
|
|
|
namespace {
|
|
/// Function object to compare source locations, putting invalid
|
|
/// locations at the end.
|
|
class CompareExprSourceLocs {
|
|
SourceManager &sourceMgr;
|
|
|
|
public:
|
|
explicit CompareExprSourceLocs(SourceManager &sourceMgr)
|
|
: sourceMgr(sourceMgr) { }
|
|
|
|
bool operator()(ASTNode lhs, ASTNode rhs) const {
|
|
if (static_cast<bool>(lhs) != static_cast<bool>(rhs)) {
|
|
return static_cast<bool>(lhs);
|
|
}
|
|
|
|
auto lhsLoc = getLoc(lhs);
|
|
auto rhsLoc = getLoc(rhs);
|
|
if (lhsLoc.isValid() != rhsLoc.isValid())
|
|
return lhsLoc.isValid();
|
|
|
|
return sourceMgr.isBeforeInBuffer(lhsLoc, rhsLoc);
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
/// Emit the fixes computed as part of the solution, returning true if we were
|
|
/// able to emit an error message, or false if none of the fixits worked out.
|
|
bool ConstraintSystem::applySolutionFixes(const Solution &solution) {
|
|
/// Collect the fixes on a per-expression basis.
|
|
llvm::SmallDenseMap<ASTNode, SmallVector<ConstraintFix *, 4>> fixesPerAnchor;
|
|
for (auto *fix : solution.Fixes) {
|
|
fixesPerAnchor[fix->getAnchor()].push_back(fix);
|
|
}
|
|
|
|
// Collect all of the expressions that have fixes, and sort them by
|
|
// source ordering.
|
|
SmallVector<ASTNode, 4> orderedAnchors;
|
|
for (const auto &fix : fixesPerAnchor) {
|
|
orderedAnchors.push_back(fix.getFirst());
|
|
}
|
|
std::sort(orderedAnchors.begin(), orderedAnchors.end(),
|
|
CompareExprSourceLocs(Context.SourceMgr));
|
|
|
|
// Walk over each of the expressions, diagnosing fixes.
|
|
bool diagnosedAnyErrors = false;
|
|
|
|
for (auto anchor : orderedAnchors) {
|
|
// Coalesce fixes with the same locator to avoid duplicating notes.
|
|
auto fixes = fixesPerAnchor[anchor];
|
|
|
|
using ConstraintFixVector = llvm::SmallVector<ConstraintFix *, 4>;
|
|
llvm::SmallMapVector<ConstraintLocator *,
|
|
llvm::SmallMapVector<FixKind, ConstraintFixVector, 4>, 4> aggregatedFixes;
|
|
for (auto *fix : fixes)
|
|
aggregatedFixes[fix->getLocator()][fix->getKind()].push_back(fix);
|
|
|
|
for (auto fixesPerLocator : aggregatedFixes) {
|
|
for (auto fixesPerKind : fixesPerLocator.second) {
|
|
auto fixes = fixesPerKind.second;
|
|
auto *primaryFix = fixes[0];
|
|
ArrayRef<ConstraintFix *> secondaryFixes{fixes.begin() + 1, fixes.end()};
|
|
|
|
auto diagnosed =
|
|
primaryFix->coalesceAndDiagnose(solution, secondaryFixes);
|
|
if (!primaryFix->isFatal()) {
|
|
assert(diagnosed && "warnings should always be diagnosed");
|
|
(void)diagnosed;
|
|
} else {
|
|
diagnosedAnyErrors |= diagnosed;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return diagnosedAnyErrors;
|
|
}
|
|
|
|
/// Pattern match if an initializer has as its value a callee that returns
|
|
/// a sent value.
|
|
static bool isSendingInitializer(Expr *initializer) {
|
|
auto *await = dyn_cast<AwaitExpr>(initializer);
|
|
if (!await)
|
|
return false;
|
|
|
|
auto *call = dyn_cast<CallExpr>(await->getSubExpr());
|
|
if (!call)
|
|
return false;
|
|
|
|
auto *fType = call->getFn()->getType()->getAs<AnyFunctionType>();
|
|
if (!fType)
|
|
return false;
|
|
|
|
return fType->hasSendingResult();
|
|
}
|
|
|
|
/// For the initializer of an `async let`, wrap it in an autoclosure and then
|
|
/// a call to that autoclosure, so that the code for the child task is captured
|
|
/// entirely within the autoclosure. This captures the semantics of the
|
|
/// operation but not the timing, e.g., the call itself will actually occur
|
|
/// when one of the variables in the async let is referenced.
|
|
static Expr *wrapAsyncLetInitializer(
|
|
ConstraintSystem &cs, Expr *initializer, DeclContext *dc) {
|
|
auto &ctx = dc->getASTContext();
|
|
|
|
// Form the autoclosure type. It is always 'async', and will be 'throws'.
|
|
Type initializerType = initializer->getType();
|
|
bool throws = TypeChecker::canThrow(ctx, initializer)
|
|
.has_value();
|
|
bool hasSendingeResult = isSendingInitializer(initializer);
|
|
bool isSendable =
|
|
!ctx.LangOpts.hasFeature(Feature::RegionBasedIsolation);
|
|
assert((isSendable || ctx.LangOpts.hasFeature(
|
|
Feature::SendingArgsAndResults)) &&
|
|
"Region Isolation should imply SendingArgsAndResults");
|
|
auto extInfo = ASTExtInfoBuilder()
|
|
.withAsync()
|
|
.withThrows(throws, /*FIXME:*/ Type())
|
|
.withSendable(isSendable)
|
|
.withSendingResult(hasSendingeResult)
|
|
.build();
|
|
|
|
// Form the autoclosure expression. The actual closure here encapsulates the
|
|
// child task.
|
|
auto closureType = FunctionType::get({ }, initializerType, extInfo);
|
|
Expr *autoclosureExpr = cs.buildAutoClosureExpr(
|
|
initializer, closureType, dc, /*isDefaultWrappedValue=*/false,
|
|
/*isAsyncLetWrapper=*/true);
|
|
|
|
// Call the autoclosure so that the AST types line up. SILGen will ignore the
|
|
// actual calls and translate them into a different mechanism.
|
|
auto autoclosureCall = CallExpr::createImplicitEmpty(ctx, autoclosureExpr);
|
|
autoclosureCall->setType(initializerType);
|
|
// FIXME: Use thrown type from above.
|
|
if (throws) {
|
|
autoclosureCall->setThrows(
|
|
ThrownErrorDestination::forMatchingContextType(
|
|
ctx.getErrorExistentialType()));
|
|
} else {
|
|
autoclosureCall->setThrows(nullptr);
|
|
}
|
|
|
|
// For a throwing expression, wrap the call in a 'try'.
|
|
Expr *resultInit = autoclosureCall;
|
|
if (throws) {
|
|
resultInit = new (ctx) TryExpr(
|
|
SourceLoc(), resultInit, initializerType, /*implicit=*/true);
|
|
}
|
|
|
|
// Wrap the call in an 'await'.
|
|
resultInit = new (ctx) AwaitExpr(
|
|
SourceLoc(), resultInit, initializerType, /*implicit=*/true);
|
|
|
|
cs.cacheExprTypes(resultInit);
|
|
return resultInit;
|
|
}
|
|
|
|
static Pattern *rewriteExprPattern(const SyntacticElementTarget &matchTarget,
|
|
Type patternTy,
|
|
SyntacticElementTargetRewriter &rewriter) {
|
|
auto *EP = matchTarget.getExprPattern();
|
|
|
|
// See if we can simplify to another kind of pattern.
|
|
if (auto simplified = TypeChecker::trySimplifyExprPattern(EP, patternTy))
|
|
return simplified.get();
|
|
|
|
auto resultTarget = rewriter.rewriteTarget(matchTarget);
|
|
if (!resultTarget)
|
|
return nullptr;
|
|
|
|
EP->setMatchExpr(resultTarget->getAsExpr());
|
|
EP->getMatchVar()->setInterfaceType(patternTy->mapTypeOutOfContext());
|
|
EP->setType(patternTy);
|
|
return EP;
|
|
}
|
|
|
|
/// Attempt to rewrite either an ExprPattern, or a pattern that was solved as
|
|
/// an ExprPattern, e.g an EnumElementPattern that could not refer to an enum
|
|
/// case.
|
|
static std::optional<Pattern *>
|
|
tryRewriteExprPattern(Pattern *P, Type patternTy,
|
|
SyntacticElementTargetRewriter &rewriter) {
|
|
// See if we have a match expression target.
|
|
auto matchTarget = rewriter.getSolution().getTargetFor(P);
|
|
if (!matchTarget)
|
|
return std::nullopt;
|
|
|
|
return rewriteExprPattern(*matchTarget, patternTy, rewriter);
|
|
}
|
|
|
|
NullablePtr<Pattern> ExprWalker::rewritePattern(Pattern *pattern,
|
|
DeclContext *DC) {
|
|
auto &solution = Rewriter.solution;
|
|
|
|
// Figure out the pattern type.
|
|
auto patternTy = solution.getResolvedType(pattern);
|
|
patternTy = patternTy->reconstituteSugar(/*recursive=*/false);
|
|
|
|
// Coerce the pattern to its appropriate type.
|
|
TypeResolutionOptions patternOptions(TypeResolverContext::InExpression);
|
|
patternOptions |= TypeResolutionFlags::OverrideType;
|
|
|
|
auto tryRewritePattern = [&](Pattern *EP, Type ty) {
|
|
return ::tryRewriteExprPattern(EP, ty, *this);
|
|
};
|
|
|
|
auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC);
|
|
return TypeChecker::coercePatternToType(contextualPattern, patternTy,
|
|
patternOptions, tryRewritePattern);
|
|
}
|
|
|
|
/// Apply the given solution to the initialization target.
|
|
///
|
|
/// \returns the resulting initialization expression.
|
|
static std::optional<SyntacticElementTarget>
|
|
applySolutionToInitialization(SyntacticElementTarget target, Expr *initializer,
|
|
SyntacticElementTargetRewriter &rewriter) {
|
|
auto &solution = rewriter.getSolution();
|
|
auto wrappedVar = target.getInitializationWrappedVar();
|
|
Type initType;
|
|
if (wrappedVar) {
|
|
initType = solution.getType(initializer);
|
|
} else {
|
|
initType = solution.getType(target.getInitializationPattern());
|
|
}
|
|
|
|
{
|
|
// Figure out what type the constraints decided on.
|
|
auto ty = solution.simplifyType(initType);
|
|
initType = ty->getRValueType()->reconstituteSugar(/*recursive =*/false);
|
|
}
|
|
|
|
// Convert the initializer to the type of the pattern.
|
|
auto &cs = solution.getConstraintSystem();
|
|
auto &ctx = cs.getASTContext();
|
|
|
|
auto locator = cs.getConstraintLocator(
|
|
target.getAsExpr(), LocatorPathElt::ContextualType(CTP_Initialization));
|
|
initializer = solution.coerceToType(initializer, initType, locator);
|
|
if (!initializer)
|
|
return std::nullopt;
|
|
|
|
SyntacticElementTarget resultTarget = target;
|
|
resultTarget.setExpr(initializer);
|
|
|
|
// Record the property wrapper type and note that the initializer has
|
|
// been subsumed by the backing property.
|
|
if (wrappedVar) {
|
|
ctx.setSideCachedPropertyWrapperBackingPropertyType(
|
|
wrappedVar, initType->mapTypeOutOfContext());
|
|
|
|
// Record the semantic initializer on the outermost property wrapper.
|
|
wrappedVar->getOutermostAttachedPropertyWrapper()->setSemanticInit(
|
|
initializer);
|
|
|
|
// If this is a wrapped parameter, we're done.
|
|
if (isa<ParamDecl>(wrappedVar))
|
|
return resultTarget;
|
|
|
|
wrappedVar->getParentPatternBinding()->setInitializerSubsumed(0);
|
|
}
|
|
|
|
// Coerce the pattern to the type of the initializer.
|
|
TypeResolutionOptions options =
|
|
isa<EditorPlaceholderExpr>(initializer->getSemanticsProvidingExpr())
|
|
? TypeResolverContext::EditorPlaceholderExpr
|
|
: target.getInitializationPatternBindingDecl()
|
|
? TypeResolverContext::PatternBindingDecl
|
|
: TypeResolverContext::InExpression;
|
|
options |= TypeResolutionFlags::OverrideType;
|
|
|
|
// Determine the type of the pattern.
|
|
Type finalPatternType = initializer->getType();
|
|
if (wrappedVar) {
|
|
if (!finalPatternType->hasError() && !finalPatternType->is<TypeVariableType>())
|
|
finalPatternType = computeWrappedValueType(wrappedVar, finalPatternType);
|
|
}
|
|
|
|
if (finalPatternType->hasDependentMember())
|
|
return std::nullopt;
|
|
|
|
finalPatternType = finalPatternType->reconstituteSugar(/*recursive =*/false);
|
|
|
|
auto tryRewritePattern = [&](Pattern *EP, Type ty) {
|
|
return ::tryRewriteExprPattern(EP, ty, rewriter);
|
|
};
|
|
|
|
// Apply the solution to the pattern as well.
|
|
auto contextualPattern = target.getContextualPattern();
|
|
if (auto coercedPattern = TypeChecker::coercePatternToType(
|
|
contextualPattern, finalPatternType, options, tryRewritePattern)) {
|
|
resultTarget.setPattern(coercedPattern);
|
|
} else {
|
|
return std::nullopt;
|
|
}
|
|
|
|
// For an async let, wrap the initializer appropriately to make it a child
|
|
// task.
|
|
if (target.isAsyncLetInitializer()) {
|
|
resultTarget.setExpr(wrapAsyncLetInitializer(
|
|
cs, resultTarget.getAsExpr(), resultTarget.getDeclContext()));
|
|
}
|
|
|
|
// If this property has an opaque result type, set the underlying type
|
|
// substitutions based on the initializer.
|
|
if (auto var = resultTarget.getInitializationPattern()->getSingleVar()) {
|
|
SubstitutionMap substitutions;
|
|
if (auto opaque = var->getOpaqueResultTypeDecl()) {
|
|
resultTarget.getAsExpr()->forEachChildExpr([&](Expr *expr) -> Expr * {
|
|
if (auto coercionExpr = dyn_cast<UnderlyingToOpaqueExpr>(expr)) {
|
|
auto newSubstitutions =
|
|
coercionExpr->substitutions.mapReplacementTypesOutOfContext();
|
|
if (substitutions.empty()) {
|
|
substitutions = newSubstitutions;
|
|
} else {
|
|
assert(substitutions.getCanonical() ==
|
|
newSubstitutions.getCanonical());
|
|
}
|
|
}
|
|
return expr;
|
|
});
|
|
|
|
opaque->setUniqueUnderlyingTypeSubstitutions(substitutions);
|
|
}
|
|
}
|
|
|
|
|
|
return resultTarget;
|
|
}
|
|
|
|
static std::optional<SequenceIterationInfo>
|
|
applySolutionToForEachStmtPreamble(ForEachStmt *stmt,
|
|
SequenceIterationInfo info, DeclContext *dc,
|
|
SyntacticElementTargetRewriter &rewriter) {
|
|
auto &solution = rewriter.getSolution();
|
|
auto &cs = solution.getConstraintSystem();
|
|
auto &ctx = cs.getASTContext();
|
|
|
|
auto *parsedSequence = stmt->getParsedSequence();
|
|
bool isAsync = stmt->getAwaitLoc().isValid();
|
|
|
|
// Simplify the various types.
|
|
info.sequenceType = solution.simplifyType(info.sequenceType);
|
|
info.elementType = solution.simplifyType(info.elementType);
|
|
info.initType = solution.simplifyType(info.initType);
|
|
|
|
// First, let's apply the solution to the expression.
|
|
auto *makeIteratorVar = info.makeIteratorVar;
|
|
|
|
auto makeIteratorTarget = *cs.getTargetFor({makeIteratorVar, /*index=*/0});
|
|
|
|
auto rewrittenTarget = rewriter.rewriteTarget(makeIteratorTarget);
|
|
if (!rewrittenTarget)
|
|
return std::nullopt;
|
|
|
|
// Set type-checked initializer and mark it as such.
|
|
{
|
|
makeIteratorVar->setInit(/*index=*/0, rewrittenTarget->getAsExpr());
|
|
makeIteratorVar->setInitializerChecked(/*index=*/0);
|
|
}
|
|
|
|
stmt->setIteratorVar(makeIteratorVar);
|
|
|
|
// Now, `$iterator.next()` call.
|
|
{
|
|
auto nextTarget = *cs.getTargetFor(info.nextCall);
|
|
|
|
auto rewrittenTarget = rewriter.rewriteTarget(nextTarget);
|
|
if (!rewrittenTarget)
|
|
return std::nullopt;
|
|
|
|
Expr *nextCall = rewrittenTarget->getAsExpr();
|
|
// Wrap a call to `next()` into `try await` since `AsyncIteratorProtocol`
|
|
// witness could be `async throws`.
|
|
if (isAsync) {
|
|
// Cannot use `forEachChildExpr` here because we need to
|
|
// to wrap a call in `try` and then stop immediately after.
|
|
struct TryInjector : ASTWalker {
|
|
ASTContext &C;
|
|
const Solution &S;
|
|
|
|
bool ShouldStop = false;
|
|
|
|
TryInjector(ASTContext &ctx, const Solution &solution)
|
|
: C(ctx), S(solution) {}
|
|
|
|
MacroWalking getMacroWalkingBehavior() const override {
|
|
return MacroWalking::Expansion;
|
|
}
|
|
|
|
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
|
|
if (ShouldStop)
|
|
return Action::Stop();
|
|
|
|
if (auto *call = dyn_cast<CallExpr>(E)) {
|
|
// There is a single call expression in `nextCall`.
|
|
ShouldStop = true;
|
|
|
|
auto nextRefType =
|
|
S.getResolvedType(call->getFn())->castTo<FunctionType>();
|
|
|
|
// If the inferred witness is throwing, we need to wrap the call
|
|
// into `try` expression.
|
|
if (nextRefType->isThrowing()) {
|
|
auto *tryExpr = TryExpr::createImplicit(
|
|
C, /*tryLoc=*/call->getStartLoc(), call, call->getType());
|
|
// Cannot stop here because we need to make sure that
|
|
// the new expression gets injected into AST.
|
|
return Action::SkipNode(tryExpr);
|
|
}
|
|
}
|
|
|
|
return Action::Continue(E);
|
|
}
|
|
};
|
|
|
|
nextCall->walk(TryInjector(ctx, solution));
|
|
}
|
|
|
|
stmt->setNextCall(nextCall);
|
|
}
|
|
|
|
// Convert that std::optional<Element> value to the type of the pattern.
|
|
auto optPatternType = OptionalType::get(info.initType);
|
|
Type nextResultType = OptionalType::get(info.elementType);
|
|
if (!optPatternType->isEqual(nextResultType)) {
|
|
OpaqueValueExpr *elementExpr = new (ctx) OpaqueValueExpr(
|
|
stmt->getInLoc(), nextResultType->getOptionalObjectType(),
|
|
/*isPlaceholder=*/true);
|
|
Expr *convertElementExpr = elementExpr;
|
|
if (TypeChecker::typeCheckExpression(convertElementExpr, dc,
|
|
/*contextualInfo=*/
|
|
{info.initType, CTP_CoerceOperand})
|
|
.isNull()) {
|
|
return std::nullopt;
|
|
}
|
|
elementExpr->setIsPlaceholder(false);
|
|
stmt->setElementExpr(elementExpr);
|
|
stmt->setConvertElementExpr(convertElementExpr);
|
|
}
|
|
|
|
// Get the conformance of the sequence type to the Sequence protocol.
|
|
auto sequenceProto = TypeChecker::getProtocol(
|
|
ctx, stmt->getForLoc(),
|
|
stmt->getAwaitLoc().isValid() ? KnownProtocolKind::AsyncSequence
|
|
: KnownProtocolKind::Sequence);
|
|
|
|
auto type = info.sequenceType->getRValueType();
|
|
if (type->isExistentialType()) {
|
|
auto *contextualLoc = solution.getConstraintLocator(
|
|
parsedSequence, LocatorPathElt::ContextualType(CTP_ForEachSequence));
|
|
type = Type(solution.OpenedExistentialTypes[contextualLoc]);
|
|
}
|
|
auto sequenceConformance = checkConformance(type, sequenceProto);
|
|
assert(!sequenceConformance.isInvalid() &&
|
|
"Couldn't find sequence conformance");
|
|
stmt->setSequenceConformance(type, sequenceConformance);
|
|
|
|
return info;
|
|
}
|
|
|
|
static std::optional<PackIterationInfo>
|
|
applySolutionToForEachStmtPreamble(ForEachStmt *stmt, PackIterationInfo info,
|
|
SyntacticElementTargetRewriter &rewriter) {
|
|
auto &solution = rewriter.getSolution();
|
|
auto &cs = solution.getConstraintSystem();
|
|
auto *sequenceExpr = stmt->getParsedSequence();
|
|
PackExpansionExpr *expansion = cast<PackExpansionExpr>(sequenceExpr);
|
|
|
|
// First, let's apply the solution to the pack expansion.
|
|
auto makeExpansionTarget = *cs.getTargetFor(expansion);
|
|
auto rewrittenTarget = rewriter.rewriteTarget(makeExpansionTarget);
|
|
if (!rewrittenTarget)
|
|
return std::nullopt;
|
|
|
|
// Simplify the pattern type of the pack expansion.
|
|
info.patternType = solution.simplifyType(info.patternType);
|
|
|
|
return info;
|
|
}
|
|
|
|
/// Apply the given solution to the for-each statement target.
|
|
///
|
|
/// \returns the resulting initialization expression.
|
|
static std::optional<SyntacticElementTarget>
|
|
applySolutionToForEachStmtPreamble(SyntacticElementTarget target,
|
|
SyntacticElementTargetRewriter &rewriter) {
|
|
auto resultTarget = target;
|
|
auto &forEachStmtInfo = resultTarget.getForEachStmtInfo();
|
|
auto *stmt = target.getAsForEachStmt();
|
|
|
|
Type rewrittenPatternType;
|
|
|
|
if (auto *info = forEachStmtInfo.dyn_cast<SequenceIterationInfo>()) {
|
|
auto resultInfo = applySolutionToForEachStmtPreamble(
|
|
stmt, *info, target.getDeclContext(), rewriter);
|
|
if (!resultInfo) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
forEachStmtInfo = *resultInfo;
|
|
rewrittenPatternType = resultInfo->initType;
|
|
} else {
|
|
auto resultInfo = applySolutionToForEachStmtPreamble(
|
|
stmt, forEachStmtInfo.get<PackIterationInfo>(), rewriter);
|
|
if (!resultInfo) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
forEachStmtInfo = *resultInfo;
|
|
rewrittenPatternType = resultInfo->patternType;
|
|
}
|
|
|
|
// Coerce the pattern to the element type.
|
|
{
|
|
TypeResolutionOptions options(TypeResolverContext::ForEachStmt);
|
|
options |= TypeResolutionFlags::OverrideType;
|
|
|
|
auto tryRewritePattern = [&](Pattern *EP, Type ty) {
|
|
return ::tryRewriteExprPattern(EP, ty, rewriter);
|
|
};
|
|
|
|
// Apply the solution to the pattern as well.
|
|
auto contextualPattern = target.getContextualPattern();
|
|
auto coercedPattern = TypeChecker::coercePatternToType(
|
|
contextualPattern, rewrittenPatternType, options, tryRewritePattern);
|
|
if (!coercedPattern)
|
|
return std::nullopt;
|
|
|
|
stmt->setPattern(coercedPattern);
|
|
resultTarget.setPattern(coercedPattern);
|
|
}
|
|
|
|
return resultTarget;
|
|
}
|
|
|
|
std::optional<SyntacticElementTarget>
|
|
ExprWalker::rewriteTarget(SyntacticElementTarget target) {
|
|
// Rewriting the target might abort in case one of visit methods returns
|
|
// nullptr. In this case, no more walkToExprPost calls are issues and thus
|
|
// nodes which were pushed on the Rewriter's ExprStack in walkToExprPre are
|
|
// not popped of the stack again in walkTokExprPost. Usually, that's not an
|
|
// issue if rewriting completely terminates because the ExprStack is never
|
|
// used again. Here, however, we recover from a rewriting failure and continue
|
|
// using the Rewriter. To make sure we don't continue with an ExprStack that
|
|
// is still in the state when rewriting was aborted, save it here and restore
|
|
// it once rewriting this target has finished.
|
|
llvm::SaveAndRestore<SmallVector<Expr *, 8>> RestoreExprStack(
|
|
Rewriter.ExprStack);
|
|
auto &solution = Rewriter.solution;
|
|
|
|
// Apply the solution to the target.
|
|
SyntacticElementTarget result = target;
|
|
if (auto expr = target.getAsExpr()) {
|
|
Expr *rewrittenExpr = expr->walk(*this);
|
|
if (!rewrittenExpr)
|
|
return std::nullopt;
|
|
|
|
/// Handle special cases for expressions.
|
|
switch (target.getExprContextualTypePurpose()) {
|
|
case CTP_Initialization: {
|
|
auto initResultTarget =
|
|
applySolutionToInitialization(target, rewrittenExpr, *this);
|
|
if (!initResultTarget)
|
|
return std::nullopt;
|
|
|
|
result = *initResultTarget;
|
|
break;
|
|
}
|
|
|
|
case CTP_Unused:
|
|
case CTP_CaseStmt:
|
|
case CTP_ReturnStmt:
|
|
case CTP_ExprPattern:
|
|
case CTP_ForEachSequence:
|
|
case CTP_YieldByValue:
|
|
case CTP_YieldByReference:
|
|
case CTP_ThrowStmt:
|
|
case CTP_DiscardStmt:
|
|
case CTP_EnumCaseRawValue:
|
|
case CTP_DefaultParameter:
|
|
case CTP_AutoclosureDefaultParameter:
|
|
case CTP_CalleeResult:
|
|
case CTP_CallArgument:
|
|
case CTP_ClosureResult:
|
|
case CTP_ArrayElement:
|
|
case CTP_DictionaryKey:
|
|
case CTP_DictionaryValue:
|
|
case CTP_CoerceOperand:
|
|
case CTP_AssignSource:
|
|
case CTP_SubscriptAssignSource:
|
|
case CTP_Condition:
|
|
case CTP_WrappedProperty:
|
|
case CTP_ComposedPropertyWrapper:
|
|
case CTP_CannotFail:
|
|
case CTP_SingleValueStmtBranch:
|
|
result.setExpr(rewrittenExpr);
|
|
break;
|
|
}
|
|
} else if (auto stmtCondition = target.getAsStmtCondition()) {
|
|
auto &cs = solution.getConstraintSystem();
|
|
|
|
for (auto &condElement : *stmtCondition) {
|
|
switch (condElement.getKind()) {
|
|
case StmtConditionElement::CK_Availability:
|
|
continue;
|
|
|
|
case StmtConditionElement::CK_HasSymbol: {
|
|
auto info = condElement.getHasSymbolInfo();
|
|
auto target = *cs.getTargetFor(&condElement);
|
|
auto resolvedTarget = rewriteTarget(target);
|
|
if (!resolvedTarget) {
|
|
info->setInvalid();
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto rewrittenExpr = resolvedTarget->getAsExpr();
|
|
info->setSymbolExpr(rewrittenExpr);
|
|
info->setReferencedDecl(
|
|
TypeChecker::getReferencedDeclForHasSymbolCondition(rewrittenExpr));
|
|
continue;
|
|
}
|
|
|
|
case StmtConditionElement::CK_Boolean: {
|
|
auto target = *cs.getTargetFor(&condElement);
|
|
auto resolvedTarget = rewriteTarget(target);
|
|
if (!resolvedTarget)
|
|
return std::nullopt;
|
|
|
|
condElement.setBoolean(resolvedTarget->getAsExpr());
|
|
continue;
|
|
}
|
|
|
|
case StmtConditionElement::CK_PatternBinding: {
|
|
auto target = *cs.getTargetFor(&condElement);
|
|
auto resolvedTarget = rewriteTarget(target);
|
|
if (!resolvedTarget)
|
|
return std::nullopt;
|
|
|
|
condElement.setInitializer(resolvedTarget->getAsExpr());
|
|
condElement.setPattern(resolvedTarget->getInitializationPattern());
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return target;
|
|
} else if (auto caseLabelItem = target.getAsCaseLabelItem()) {
|
|
ConstraintSystem &cs = solution.getConstraintSystem();
|
|
auto info = *cs.getCaseLabelItemInfo(*caseLabelItem);
|
|
|
|
auto pattern = rewritePattern(info.pattern, target.getDeclContext());
|
|
if (!pattern)
|
|
return std::nullopt;
|
|
|
|
(*caseLabelItem)->setPattern(pattern.get(), /*resolved=*/true);
|
|
|
|
// If there is a guard expression, coerce that.
|
|
if (auto *guardExpr = info.guardExpr) {
|
|
auto target = *cs.getTargetFor(guardExpr);
|
|
auto resultTarget = rewriteTarget(target);
|
|
if (!resultTarget)
|
|
return std::nullopt;
|
|
|
|
(*caseLabelItem)->setGuardExpr(resultTarget->getAsExpr());
|
|
}
|
|
|
|
return target;
|
|
} else if (auto patternBinding = target.getAsPatternBinding()) {
|
|
ConstraintSystem &cs = solution.getConstraintSystem();
|
|
for (unsigned index : range(patternBinding->getNumPatternEntries())) {
|
|
if (patternBinding->isInitializerChecked(index))
|
|
continue;
|
|
|
|
// Find the target for this.
|
|
auto knownTarget = *cs.getTargetFor({patternBinding, index});
|
|
|
|
// Rewrite the target.
|
|
auto resultTarget = rewriteTarget(knownTarget);
|
|
if (!resultTarget)
|
|
return std::nullopt;
|
|
|
|
auto *pattern = resultTarget->getInitializationPattern();
|
|
// Record that the pattern has been fully validated,
|
|
// this is important for subsequent call to typeCheckDecl
|
|
// because otherwise it would try to re-typecheck pattern.
|
|
patternBinding->setPattern(index, pattern,
|
|
/*isFullyValidated=*/true);
|
|
|
|
if (patternBinding->isExplicitlyInitialized(index) ||
|
|
(patternBinding->isDefaultInitializable(index) &&
|
|
pattern->hasStorage())) {
|
|
patternBinding->setInit(index, resultTarget->getAsExpr());
|
|
patternBinding->setInitializerChecked(index);
|
|
}
|
|
}
|
|
|
|
return target;
|
|
} else if (auto *wrappedVar = target.getAsUninitializedWrappedVar()) {
|
|
// Get the outermost wrapper type from the solution
|
|
auto outermostWrapper = wrappedVar->getOutermostAttachedPropertyWrapper();
|
|
auto backingType = solution.simplifyType(
|
|
solution.getType(outermostWrapper->getTypeExpr()));
|
|
|
|
auto &ctx = solution.getConstraintSystem().getASTContext();
|
|
ctx.setSideCachedPropertyWrapperBackingPropertyType(
|
|
wrappedVar, backingType->mapTypeOutOfContext());
|
|
|
|
return target;
|
|
} else if (target.getAsUninitializedVar()) {
|
|
TypeResolutionOptions options(TypeResolverContext::PatternBindingDecl);
|
|
|
|
auto contextualPattern = target.getContextualPattern();
|
|
auto patternType = target.getTypeOfUninitializedVar();
|
|
auto *PB = target.getPatternBindingOfUninitializedVar();
|
|
|
|
// If this is a placeholder variable, let's override its type with
|
|
// inferred one.
|
|
if (isPlaceholderVar(PB)) {
|
|
patternType = solution.getResolvedType(PB->getSingleVar());
|
|
options |= TypeResolutionFlags::OverrideType;
|
|
}
|
|
|
|
auto tryRewritePattern = [&](Pattern *EP, Type ty) {
|
|
return ::tryRewriteExprPattern(EP, ty, *this);
|
|
};
|
|
|
|
if (auto coercedPattern = TypeChecker::coercePatternToType(
|
|
contextualPattern, patternType, options, tryRewritePattern)) {
|
|
auto resultTarget = target;
|
|
resultTarget.setPattern(coercedPattern);
|
|
return resultTarget;
|
|
}
|
|
|
|
return std::nullopt;
|
|
} else if (target.getAsForEachStmt()) {
|
|
auto forEachPreambleResultTarget =
|
|
applySolutionToForEachStmtPreamble(target, *this);
|
|
if (!forEachPreambleResultTarget)
|
|
return std::nullopt;
|
|
|
|
result = *forEachPreambleResultTarget;
|
|
} else {
|
|
auto fn = *target.getAsFunction();
|
|
if (rewriteFunction(fn))
|
|
return std::nullopt;
|
|
|
|
result.setFunctionBody(fn.getBody());
|
|
}
|
|
|
|
// Follow-up tasks.
|
|
auto &cs = solution.getConstraintSystem();
|
|
if (auto resultExpr = result.getAsExpr()) {
|
|
Expr *expr = target.getAsExpr();
|
|
assert(expr && "Can't have expression result without expression target");
|
|
|
|
// We are supposed to use contextual type only if it is present and
|
|
// this expression doesn't represent the implicit return of the single
|
|
// expression function which got deduced to be `Never`.
|
|
Type convertType = target.getExprConversionType();
|
|
auto shouldCoerceToContextualType = [&]() {
|
|
if (!convertType)
|
|
return false;
|
|
|
|
if (convertType->hasPlaceholder())
|
|
return false;
|
|
|
|
if (target.isOptionalSomePatternInit())
|
|
return false;
|
|
|
|
if (solution.simplifyType(convertType)->isVoid()) {
|
|
auto contextPurpose = cs.getContextualTypePurpose(target.getAsExpr());
|
|
if (contextPurpose == CTP_ClosureResult ||
|
|
contextPurpose == CTP_SingleValueStmtBranch) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// If we're supposed to convert the expression to some particular type,
|
|
// do so now.
|
|
if (shouldCoerceToContextualType()) {
|
|
auto contextualTypePurpose = target.getExprContextualTypePurpose();
|
|
|
|
auto *locator = target.getExprConvertTypeLocator();
|
|
if (!locator) {
|
|
locator = cs.getConstraintLocator(
|
|
expr, LocatorPathElt::ContextualType(contextualTypePurpose));
|
|
}
|
|
assert(locator);
|
|
|
|
resultExpr = Rewriter.coerceToType(
|
|
resultExpr, solution.simplifyType(convertType), locator);
|
|
} else if (cs.getType(resultExpr)->hasLValueType() &&
|
|
!target.isDiscardedExpr()) {
|
|
// We referenced an lvalue. Load it.
|
|
resultExpr = Rewriter.coerceToType(
|
|
resultExpr, cs.getType(resultExpr)->getRValueType(),
|
|
cs.getConstraintLocator(expr,
|
|
LocatorPathElt::ContextualType(
|
|
target.getExprContextualTypePurpose())));
|
|
}
|
|
|
|
if (!resultExpr)
|
|
return std::nullopt;
|
|
|
|
// For an @autoclosure default parameter type, add the autoclosure
|
|
// conversion.
|
|
if (FunctionType *autoclosureParamType =
|
|
target.getAsAutoclosureParamType()) {
|
|
resultExpr = cs.buildAutoClosureExpr(resultExpr, autoclosureParamType,
|
|
target.getDeclContext());
|
|
}
|
|
|
|
solution.setExprTypes(resultExpr);
|
|
result.setExpr(resultExpr);
|
|
|
|
if (cs.isDebugMode()) {
|
|
auto &log = llvm::errs();
|
|
log << "\n---Type-checked expression---\n";
|
|
resultExpr->dump(log);
|
|
log << "\n";
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// Apply a given solution to the expression, producing a fully
|
|
/// type-checked expression.
|
|
std::optional<SyntacticElementTarget>
|
|
ConstraintSystem::applySolution(Solution &solution,
|
|
SyntacticElementTarget target) {
|
|
// If any fixes needed to be applied to arrive at this solution, resolve
|
|
// them to specific expressions.
|
|
unsigned numResolvableFixes = 0;
|
|
if (!solution.Fixes.empty()) {
|
|
if (shouldSuppressDiagnostics())
|
|
return std::nullopt;
|
|
|
|
bool diagnosedErrorsViaFixes = applySolutionFixes(solution);
|
|
bool canApplySolution = true;
|
|
for (const auto fix : solution.Fixes) {
|
|
if (fix->isFatal())
|
|
canApplySolution = false;
|
|
if (fix->impact() == SK_Fix && !fix->isFatal())
|
|
++numResolvableFixes;
|
|
}
|
|
|
|
// If all of the available fixes would result in a warning,
|
|
// we can go ahead and apply this solution to AST.
|
|
if (!canApplySolution) {
|
|
// If we already diagnosed any errors via fixes, that's it.
|
|
if (diagnosedErrorsViaFixes)
|
|
return std::nullopt;
|
|
|
|
// If we didn't manage to diagnose anything well, so fall back to
|
|
// diagnosing mining the system to construct a reasonable error message.
|
|
diagnoseFailureFor(target);
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
// If there are no fixes recorded but score indicates that there
|
|
// should have been at least one, let's fail application and
|
|
// produce a fallback diagnostic to highlight the problem.
|
|
{
|
|
const auto &score = solution.getFixedScore();
|
|
if (score.Data[SK_Fix] > numResolvableFixes || score.Data[SK_Hole] > 0) {
|
|
maybeProduceFallbackDiagnostic(target.getLoc());
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
// If the score indicates the solution is valid, ensure we don't have any
|
|
// unresolved types.
|
|
{
|
|
auto isValidType = [&](Type ty) {
|
|
return !ty->hasUnresolvedType() && !ty->hasError() &&
|
|
!ty->hasTypeVariableOrPlaceholder();
|
|
};
|
|
for (auto &[_, type] : solution.typeBindings) {
|
|
ASSERT(isValidType(type) && "type binding has invalid type");
|
|
}
|
|
for (auto &[_, type] : solution.nodeTypes) {
|
|
ASSERT(isValidType(solution.simplifyType(type)) &&
|
|
"node type has invalid type");
|
|
}
|
|
for (auto &[_, type] : solution.keyPathComponentTypes) {
|
|
ASSERT(isValidType(solution.simplifyType(type)) &&
|
|
"key path type has invalid type");
|
|
}
|
|
}
|
|
|
|
ExprRewriter rewriter(*this, solution, target, shouldSuppressDiagnostics());
|
|
ExprWalker walker(rewriter);
|
|
auto resultTarget = walker.rewriteTarget(target);
|
|
if (!resultTarget)
|
|
return std::nullopt;
|
|
|
|
rewriter.finalize();
|
|
|
|
return resultTarget;
|
|
}
|
|
|
|
Expr *
|
|
Solution::coerceToType(Expr *expr, Type toType, ConstraintLocator *locator) {
|
|
auto &cs = getConstraintSystem();
|
|
ExprRewriter rewriter(cs, *this, std::nullopt, /*suppressDiagnostics=*/false);
|
|
Expr *result = rewriter.coerceToType(expr, toType, locator);
|
|
if (!result)
|
|
return nullptr;
|
|
|
|
setExprTypes(result);
|
|
rewriter.finalize();
|
|
return result;
|
|
}
|
|
|
|
bool Solution::hasType(ASTNode node) const {
|
|
auto result = nodeTypes.find(node);
|
|
if (result != nodeTypes.end())
|
|
return true;
|
|
|
|
auto &cs = getConstraintSystem();
|
|
return cs.hasType(node);
|
|
}
|
|
|
|
bool Solution::hasType(const KeyPathExpr *KP, unsigned ComponentIndex) const {
|
|
assert(KP && "Expected non-null key path parameter!");
|
|
return keyPathComponentTypes.find(std::make_pair(KP, ComponentIndex))
|
|
!= keyPathComponentTypes.end();
|
|
}
|
|
|
|
Type Solution::getType(ASTNode node) const {
|
|
auto result = nodeTypes.find(node);
|
|
if (result != nodeTypes.end())
|
|
return result->second;
|
|
|
|
auto &cs = getConstraintSystem();
|
|
return cs.getType(node);
|
|
}
|
|
|
|
Type Solution::getType(const KeyPathExpr *KP, unsigned I) const {
|
|
assert(hasType(KP, I) && "Expected type to have been set!");
|
|
return keyPathComponentTypes.find(std::make_pair(KP, I))->second;
|
|
}
|
|
|
|
TypeVariableType *
|
|
Solution::getKeyPathRootType(const KeyPathExpr *keyPath) const {
|
|
auto result = getKeyPathRootTypeIfAvailable(keyPath);
|
|
assert(result);
|
|
return result;
|
|
}
|
|
|
|
TypeVariableType *
|
|
Solution::getKeyPathRootTypeIfAvailable(const KeyPathExpr *keyPath) const {
|
|
auto result = KeyPaths.find(keyPath);
|
|
if (result != KeyPaths.end())
|
|
return std::get<0>(result->second);
|
|
return nullptr;
|
|
}
|
|
|
|
Type Solution::getResolvedType(ASTNode node) const {
|
|
return simplifyType(getType(node));
|
|
}
|
|
|
|
void Solution::setExprTypes(Expr *expr) const {
|
|
if (!expr)
|
|
return;
|
|
|
|
SetExprTypes SET(*this);
|
|
expr->walk(SET);
|
|
}
|
|
|
|
/// MARK: SolutionResult implementation.
|
|
|
|
SolutionResult SolutionResult::forSolved(Solution &&solution) {
|
|
SolutionResult result(Kind::Success);
|
|
void *memory = malloc(sizeof(Solution));
|
|
result.solutions = new (memory) Solution(std::move(solution));
|
|
result.numSolutions = 1;
|
|
return result;
|
|
}
|
|
|
|
SolutionResult SolutionResult::forAmbiguous(
|
|
MutableArrayRef<Solution> solutions) {
|
|
assert(solutions.size() > 1 && "Not actually ambiguous");
|
|
SolutionResult result(Kind::Ambiguous);
|
|
result.solutions =
|
|
(Solution *)malloc(sizeof(Solution) * solutions.size());
|
|
result.numSolutions = solutions.size();
|
|
std::uninitialized_copy(std::make_move_iterator(solutions.begin()),
|
|
std::make_move_iterator(solutions.end()),
|
|
result.solutions);
|
|
return result;
|
|
}
|
|
|
|
SolutionResult
|
|
SolutionResult::forTooComplex(std::optional<SourceRange> affected) {
|
|
SolutionResult result(Kind::TooComplex);
|
|
result.TooComplexAt = affected;
|
|
return result;
|
|
}
|
|
|
|
SolutionResult::~SolutionResult() {
|
|
assert((!requiresDiagnostic() || emittedDiagnostic) &&
|
|
"SolutionResult was destroyed without emitting a diagnostic");
|
|
|
|
for (unsigned i : range(numSolutions)) {
|
|
solutions[i].~Solution();
|
|
}
|
|
free(solutions);
|
|
}
|
|
|
|
const Solution &SolutionResult::getSolution() const {
|
|
assert(numSolutions == 1 && "Wrong number of solutions");
|
|
return solutions[0];
|
|
}
|
|
|
|
Solution &&SolutionResult::takeSolution() && {
|
|
assert(numSolutions == 1 && "Wrong number of solutions");
|
|
return std::move(solutions[0]);
|
|
}
|
|
|
|
ArrayRef<Solution> SolutionResult::getAmbiguousSolutions() const {
|
|
assert(getKind() == Ambiguous);
|
|
return llvm::ArrayRef(solutions, numSolutions);
|
|
}
|
|
|
|
MutableArrayRef<Solution> SolutionResult::takeAmbiguousSolutions() && {
|
|
assert(getKind() == Ambiguous);
|
|
markAsDiagnosed();
|
|
return MutableArrayRef<Solution>(solutions, numSolutions);
|
|
}
|