Files
swift-mirror/lib/Sema/PreCheckTarget.cpp
Hamish Knight f95eb6bf48 [Sema] Tighten up function call check in resolveKeyPathExpr
Check for `CallExpr` instead of `ApplyExpr`, we don't support
arbitrary postfix operators in key paths.
2025-06-17 23:07:55 +01:00

2736 lines
99 KiB
C++

//===--- PreCheckTarget.cpp - Pre-checking pass ---------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Pre-checking resolves unqualified name references, type expressions and
// operators.
//
//===----------------------------------------------------------------------===//
#include "TypeChecker.h"
#include "TypeCheckConcurrency.h"
#include "TypeCheckType.h"
#include "TypoCorrection.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/ClangModuleLoader.h"
#include "swift/AST/ConformanceLookup.h"
#include "swift/AST/DiagnosticsParse.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/NameLookupRequests.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/PropertyWrappers.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/Assertions.h"
#include "swift/Parse/Confusables.h"
#include "swift/Parse/Lexer.h"
#include "swift/Sema/ConstraintSystem.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
using namespace swift;
using namespace constraints;
//===----------------------------------------------------------------------===//
// High-level entry points.
//===----------------------------------------------------------------------===//
static unsigned getNumArgs(ValueDecl *value) {
if (auto *func = dyn_cast<FuncDecl>(value))
return func->getParameters()->size();
return ~0U;
}
static bool matchesDeclRefKind(ValueDecl *value, DeclRefKind refKind) {
switch (refKind) {
// An ordinary reference doesn't ignore anything.
case DeclRefKind::Ordinary:
return true;
// A binary-operator reference only honors FuncDecls with a certain type.
case DeclRefKind::BinaryOperator:
return (getNumArgs(value) == 2);
case DeclRefKind::PrefixOperator:
return (!value->getAttrs().hasAttribute<PostfixAttr>() &&
getNumArgs(value) == 1);
case DeclRefKind::PostfixOperator:
return (value->getAttrs().hasAttribute<PostfixAttr>() &&
getNumArgs(value) == 1);
}
llvm_unreachable("bad declaration reference kind");
}
static bool containsDeclRefKind(LookupResult &lookupResult,
DeclRefKind refKind) {
for (auto candidate : lookupResult) {
ValueDecl *D = candidate.getValueDecl();
if (!D)
continue;
if (matchesDeclRefKind(D, refKind))
return true;
}
return false;
}
/// Emit a diagnostic with a fixit hint for an invalid binary operator, showing
/// how to split it according to splitCandidate.
static void diagnoseBinOpSplit(ASTContext &Context, UnresolvedDeclRefExpr *UDRE,
std::pair<unsigned, bool> splitCandidate,
Diag<Identifier, Identifier, bool> diagID) {
unsigned splitLoc = splitCandidate.first;
bool isBinOpFirst = splitCandidate.second;
StringRef nameStr = UDRE->getName().getBaseIdentifier().str();
auto startStr = nameStr.substr(0, splitLoc);
auto endStr = nameStr.drop_front(splitLoc);
// One valid split found, it is almost certainly the right answer.
auto diag = Context.Diags.diagnose(
UDRE->getLoc(), diagID, Context.getIdentifier(startStr),
Context.getIdentifier(endStr), isBinOpFirst);
// Highlight the whole operator.
diag.highlight(UDRE->getLoc());
// Insert whitespace on the left if the binop is at the start, or to the
// right if it is end.
if (isBinOpFirst)
diag.fixItInsert(UDRE->getLoc(), " ");
else
diag.fixItInsertAfter(UDRE->getLoc(), " ");
// Insert a space between the operators.
diag.fixItInsert(UDRE->getLoc().getAdvancedLoc(splitLoc), " ");
}
/// If we failed lookup of a binary operator, check to see it to see if
/// it is a binary operator juxtaposed with a unary operator (x*-4) that
/// needs whitespace. If so, emit specific diagnostics for it and return true,
/// otherwise return false.
static bool diagnoseOperatorJuxtaposition(UnresolvedDeclRefExpr *UDRE,
DeclContext *DC) {
Identifier name = UDRE->getName().getBaseIdentifier();
StringRef nameStr = name.str();
if (!name.isOperator() || nameStr.size() < 2)
return false;
bool isBinOp = UDRE->getRefKind() == DeclRefKind::BinaryOperator;
// If this is a binary operator, relex the token, to decide whether it has
// whitespace around it or not. If it does "x +++ y", then it isn't likely to
// be a case where a space was forgotten.
auto &Context = DC->getASTContext();
if (isBinOp) {
auto tok = Lexer::getTokenAtLocation(Context.SourceMgr, UDRE->getLoc());
if (tok.getKind() != tok::oper_binary_unspaced)
return false;
}
// Okay, we have a failed lookup of a multicharacter operator. Check to see if
// lookup succeeds if part is split off, and record the matches found.
//
// In the case of a binary operator, the bool indicated is false if the
// first half of the split is the unary operator (x!*4) or true if it is the
// binary operator (x*+4).
std::vector<std::pair<unsigned, bool>> WorkableSplits;
// Check all the potential splits.
for (unsigned splitLoc = 1, e = nameStr.size(); splitLoc != e; ++splitLoc) {
// For it to be a valid split, the start and end section must be valid
// operators, splitting a unicode code point isn't kosher.
auto startStr = nameStr.substr(0, splitLoc);
auto endStr = nameStr.drop_front(splitLoc);
if (!Lexer::isOperator(startStr) || !Lexer::isOperator(endStr))
continue;
DeclNameRef startName(Context.getIdentifier(startStr));
DeclNameRef endName(Context.getIdentifier(endStr));
// Perform name lookup for the first and second pieces. If either fail to
// be found, then it isn't a valid split.
auto startLookup = TypeChecker::lookupUnqualified(
DC, startName, UDRE->getLoc(), defaultUnqualifiedLookupOptions);
if (!startLookup) continue;
auto endLookup = TypeChecker::lookupUnqualified(DC, endName, UDRE->getLoc(),
defaultUnqualifiedLookupOptions);
if (!endLookup) continue;
// If the overall operator is a binary one, then we're looking at
// juxtaposed binary and unary operators.
if (isBinOp) {
// Look to see if the candidates found could possibly match.
if (containsDeclRefKind(startLookup, DeclRefKind::PostfixOperator) &&
containsDeclRefKind(endLookup, DeclRefKind::BinaryOperator))
WorkableSplits.push_back({ splitLoc, false });
if (containsDeclRefKind(startLookup, DeclRefKind::BinaryOperator) &&
containsDeclRefKind(endLookup, DeclRefKind::PrefixOperator))
WorkableSplits.push_back({ splitLoc, true });
} else {
// Otherwise, it is two of the same kind, e.g. "!!x" or "!~x".
if (containsDeclRefKind(startLookup, UDRE->getRefKind()) &&
containsDeclRefKind(endLookup, UDRE->getRefKind()))
WorkableSplits.push_back({ splitLoc, false });
}
}
switch (WorkableSplits.size()) {
case 0:
// No splits found, can't produce this diagnostic.
return false;
case 1:
// One candidate: produce an error with a fixit on it.
if (isBinOp)
diagnoseBinOpSplit(Context, UDRE, WorkableSplits[0],
diag::unspaced_binary_operator_fixit);
else
Context.Diags.diagnose(
UDRE->getLoc().getAdvancedLoc(WorkableSplits[0].first),
diag::unspaced_unary_operator);
return true;
default:
// Otherwise, we have to produce a series of notes listing the various
// options.
Context.Diags
.diagnose(UDRE->getLoc(), isBinOp ? diag::unspaced_binary_operator
: diag::unspaced_unary_operator)
.highlight(UDRE->getLoc());
if (isBinOp) {
for (auto candidateSplit : WorkableSplits)
diagnoseBinOpSplit(Context, UDRE, candidateSplit,
diag::unspaced_binary_operators_candidate);
}
return true;
}
}
static bool diagnoseRangeOperatorMisspell(DiagnosticEngine &Diags,
UnresolvedDeclRefExpr *UDRE) {
auto name = UDRE->getName().getBaseIdentifier();
if (!name.isOperator())
return false;
auto corrected = StringRef();
if (name.str() == ".." || name.str() == "...." ||
name.str() == ".…" || name.str() == "" || name.str() == "….")
corrected = "...";
else if (name.str() == "...<" || name.str() == "....<" ||
name.str() == "…<")
corrected = "..<";
if (!corrected.empty()) {
Diags
.diagnose(UDRE->getLoc(), diag::cannot_find_in_scope_corrected,
UDRE->getName(), true, corrected)
.highlight(UDRE->getSourceRange())
.fixItReplace(UDRE->getSourceRange(), corrected);
return true;
}
return false;
}
static bool diagnoseNonexistentPowerOperator(DiagnosticEngine &Diags,
UnresolvedDeclRefExpr *UDRE,
DeclContext *DC) {
auto name = UDRE->getName().getBaseIdentifier();
if (!(name.isOperator() && name.is("**")))
return false;
DC = DC->getModuleScopeContext();
auto &ctx = DC->getASTContext();
DeclNameRef powerName(ctx.getIdentifier("pow"));
// Look if 'pow(_:_:)' exists within current context.
auto lookUp = TypeChecker::lookupUnqualified(
DC, powerName, UDRE->getLoc(), defaultUnqualifiedLookupOptions);
if (lookUp) {
Diags.diagnose(UDRE->getLoc(), diag::nonexistent_power_operator)
.highlight(UDRE->getSourceRange());
return true;
}
return false;
}
static bool diagnoseIncDecOperator(DiagnosticEngine &Diags,
UnresolvedDeclRefExpr *UDRE) {
auto name = UDRE->getName().getBaseIdentifier();
if (!name.isOperator())
return false;
auto corrected = StringRef();
if (name.str() == "++")
corrected = "+= 1";
else if (name.str() == "--")
corrected = "-= 1";
if (!corrected.empty()) {
Diags
.diagnose(UDRE->getLoc(), diag::cannot_find_in_scope_corrected,
UDRE->getName(), true, corrected)
.highlight(UDRE->getSourceRange());
return true;
}
return false;
}
static bool findNonMembers(ArrayRef<LookupResultEntry> lookupResults,
DeclRefKind refKind, bool breakOnMember,
SmallVectorImpl<ValueDecl *> &ResultValues,
llvm::function_ref<bool(ValueDecl *)> isValid) {
bool AllDeclRefs = true;
for (auto Result : lookupResults) {
// If we find a member, then all of the results aren't non-members.
bool IsMember =
(Result.getBaseDecl() && !isa<ModuleDecl>(Result.getBaseDecl()));
if (IsMember) {
AllDeclRefs = false;
if (breakOnMember)
break;
continue;
}
ValueDecl *D = Result.getValueDecl();
if (!isValid(D))
return false;
if (matchesDeclRefKind(D, refKind))
ResultValues.push_back(D);
}
return AllDeclRefs;
}
namespace {
enum class MemberChainKind {
OptionalBind, // A 'x?.y' optional binding chain
UnresolvedMember, // A '.foo.bar' chain
};
} // end anonymous namespace
/// Find the next element in a chain of members. If this expression is (or
/// could be) the base of such a chain, this will return \c nullptr.
static Expr *getMemberChainSubExpr(Expr *expr, MemberChainKind kind) {
assert(expr && "getMemberChainSubExpr called with null expr!");
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(expr))
return UDE->getBase();
if (auto *CE = dyn_cast<CallExpr>(expr))
return CE->getFn();
if (auto *BOE = dyn_cast<BindOptionalExpr>(expr))
return BOE->getSubExpr();
if (auto *FVE = dyn_cast<ForceValueExpr>(expr))
return FVE->getSubExpr();
if (auto *SE = dyn_cast<SubscriptExpr>(expr))
return SE->getBase();
if (auto *DSE = dyn_cast<DotSelfExpr>(expr))
return DSE->getSubExpr();
if (auto *USE = dyn_cast<UnresolvedSpecializeExpr>(expr))
return USE->getSubExpr();
if (auto *CCE = dyn_cast<CodeCompletionExpr>(expr))
return CCE->getBase();
if (kind == MemberChainKind::OptionalBind) {
// We allow postfix operators to be part of the optional member chain, e.g:
//
// for?.bar++
// x.y?^.foo()
//
// Note this behavior is specific to optional chains, we treat e.g
// `.foo^` as `(.foo)^`.
if (auto *PO = dyn_cast<PostfixUnaryExpr>(expr))
return PO->getOperand();
// Unresolved member chains can themselves be nested in optional chains
// since optional chains can include postfix operators.
if (auto *UME = dyn_cast<UnresolvedMemberChainResultExpr>(expr))
return UME->getSubExpr();
}
return nullptr;
}
UnresolvedMemberExpr *TypeChecker::getUnresolvedMemberChainBase(Expr *expr) {
if (auto *subExpr =
getMemberChainSubExpr(expr, MemberChainKind::UnresolvedMember)) {
return getUnresolvedMemberChainBase(subExpr);
}
return dyn_cast<UnresolvedMemberExpr>(expr);
}
static bool isBindOptionalMemberChain(Expr *expr) {
if (isa<BindOptionalExpr>(expr))
return true;
if (auto *base = getMemberChainSubExpr(expr, MemberChainKind::OptionalBind))
return isBindOptionalMemberChain(base);
return false;
}
/// Whether this expression sits at the end of a chain of member accesses.
static bool isMemberChainTail(Expr *expr, Expr *parent, MemberChainKind kind) {
assert(expr && "isMemberChainTail called with null expr!");
// If this expression's parent is not itself part of a chain (or, this expr
// has no parent expr), this must be the tail of the chain.
return !parent || getMemberChainSubExpr(parent, kind) != expr;
}
static bool isValidForwardReference(ValueDecl *D, DeclContext *DC,
ValueDecl **localDeclAfterUse) {
*localDeclAfterUse = nullptr;
// References to variables injected by lldb are always valid.
if (isa<VarDecl>(D) && cast<VarDecl>(D)->isDebuggerVar())
return true;
// If we find something in the current context, it must be a forward
// reference, because otherwise if it was in scope, it would have
// been returned by the call to ASTScope::lookupLocalDecls() above.
if (D->getDeclContext()->isLocalContext()) {
do {
if (D->getDeclContext() == DC) {
*localDeclAfterUse = D;
return false;
}
// If we're inside of a 'defer' context, walk up to the parent
// and check again. We don't want 'defer' bodies to forward
// reference bindings in the immediate outer scope.
} while (isa<FuncDecl>(DC) &&
cast<FuncDecl>(DC)->isDeferBody() &&
(DC = DC->getParent()));
}
return true;
}
/// Checks whether this is a BinaryExpr with operator `&` and returns the
/// BinaryExpr, if so.
static BinaryExpr *getCompositionExpr(Expr *expr) {
if (auto *binaryExpr = dyn_cast<BinaryExpr>(expr)) {
// look at the name of the operator, if it is a '&' we can create the
// composition TypeExpr
auto fn = binaryExpr->getFn();
if (auto Overload = dyn_cast<OverloadedDeclRefExpr>(fn)) {
if (llvm::any_of(Overload->getDecls(), [](auto *decl) -> bool {
return decl->getBaseName() == "&";
}))
return binaryExpr;
} else if (auto *Decl = dyn_cast<UnresolvedDeclRefExpr>(fn)) {
if (Decl->getName().isSimpleName() &&
Decl->getName().getBaseName() == "&")
return binaryExpr;
}
}
return nullptr;
}
/// Diagnoses an unqualified `init` expression.
///
/// \param initExpr The \c init expression.
/// \param dc The declaration context of \p initExpr.
///
/// \returns An expression matching `self.init` or `super.init` that can be used
/// to recover, or `nullptr` if cannot recover.
static UnresolvedDotExpr *
diagnoseUnqualifiedInit(UnresolvedDeclRefExpr *initExpr, DeclContext *dc,
ASTContext &ctx) {
const auto loc = initExpr->getLoc();
enum class Suggestion : unsigned {
None = 0,
Self = 1,
Super = 2,
};
Suggestion suggestion = [dc]() {
NominalTypeDecl *nominal = nullptr;
{
auto *typeDC = dc->getInnermostTypeContext();
if (!typeDC) {
// No type context--no suggestion.
return Suggestion::None;
}
nominal = typeDC->getSelfNominalTypeDecl();
}
auto *classDecl = dyn_cast<ClassDecl>(nominal);
if (!classDecl || !classDecl->hasSuperclass()) {
// No class or no superclass--suggest 'self.'.
return Suggestion::Self;
}
if (auto *initDecl = dyn_cast<ConstructorDecl>(dc)) {
if (initDecl->getAttrs().hasAttribute<ConvenienceAttr>()) {
// Innermost context is a convenience initializer--suggest 'self.'.
return Suggestion::Self;
} else {
// Innermost context is a designated initializer--suggest 'super.'.
return Suggestion::Super;
}
}
// Class context but innermost context is not an initializer--suggest
// 'self.'. 'super.' might be possible too, but is far lesss likely to be
// the right answer.
return Suggestion::Self;
}();
auto diag =
ctx.Diags.diagnose(loc, diag::unqualified_init, (unsigned)suggestion);
Expr *base = nullptr;
switch (suggestion) {
case Suggestion::None:
return nullptr;
case Suggestion::Self:
diag.fixItInsert(loc, "self.");
base = new (ctx)
UnresolvedDeclRefExpr(DeclNameRef(ctx.Id_self), DeclRefKind::Ordinary,
initExpr->getNameLoc());
base->setImplicit(true);
break;
case Suggestion::Super:
diag.fixItInsert(loc, "super.");
base = new (ctx) SuperRefExpr(/*Self=*/nullptr, loc, /*Implicit=*/true);
break;
}
return new (ctx)
UnresolvedDotExpr(base, /*dotloc=*/SourceLoc(), initExpr->getName(),
initExpr->getNameLoc(), /*implicit=*/true);
}
/// Bind an UnresolvedDeclRefExpr by performing name lookup and
/// returning the resultant expression. Context is the DeclContext used
/// for the lookup.
Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
DeclContext *DC) {
auto &Context = DC->getASTContext();
DeclNameRef Name = UDRE->getName();
SourceLoc Loc = UDRE->getLoc();
auto errorResult = [&]() -> Expr * {
return new (Context) ErrorExpr(UDRE->getSourceRange());
};
TypeChecker::checkForForbiddenPrefix(Context, Name.getBaseName());
// Try and recover if we have an unqualified 'init'.
if (Name.getBaseName().isConstructor()) {
auto *recoveryExpr = diagnoseUnqualifiedInit(UDRE, DC, Context);
if (!recoveryExpr)
return errorResult();
return recoveryExpr;
}
// Process UnresolvedDeclRefExpr by doing an unqualified lookup.
DeclNameRef LookupName = Name;
if (Name.isCompoundName()) {
auto &context = DC->getASTContext();
// Remove any $ prefixes for lookup
SmallVector<Identifier, 4> lookupLabels;
for (auto label : Name.getArgumentNames()) {
if (label.hasDollarPrefix()) {
auto unprefixed = label.str().drop_front();
lookupLabels.push_back(context.getIdentifier(unprefixed));
} else {
lookupLabels.push_back(label);
}
}
DeclName lookupName(context, Name.getBaseName(), lookupLabels);
LookupName = DeclNameRef(lookupName);
}
// Perform standard value name lookup.
NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions;
// TODO: Include all of the possible members to give a solver a
// chance to diagnose name shadowing which requires explicit
// name/module qualifier to access top-level name.
lookupOptions |= NameLookupFlags::IncludeOuterResults;
LookupResult Lookup;
bool AllDeclRefs = true;
SmallVector<ValueDecl*, 4> ResultValues;
// First, look for a local binding in scope.
if (Loc.isValid() && !Name.isOperator()) {
ASTScope::lookupLocalDecls(DC->getParentSourceFile(),
LookupName.getFullName(), Loc,
/*stopAfterInnermostBraceStmt=*/false,
ResultValues);
for (auto *localDecl : ResultValues) {
Lookup.add(LookupResultEntry(localDecl), /*isOuter=*/false);
}
}
if (!Lookup) {
// Now, look for all local bindings, even forward references, as well
// as type members and top-level declarations.
if (Loc.isInvalid())
DC = DC->getModuleScopeContext();
Lookup = TypeChecker::lookupUnqualified(DC, LookupName, Loc, lookupOptions);
ValueDecl *localDeclAfterUse = nullptr;
AllDeclRefs =
findNonMembers(Lookup.innerResults(), UDRE->getRefKind(),
/*breakOnMember=*/true, ResultValues,
[&](ValueDecl *D) {
return isValidForwardReference(D, DC, &localDeclAfterUse);
});
// If local declaration after use is found, check outer results for
// better matching candidates.
if (ResultValues.empty() && localDeclAfterUse) {
auto innerDecl = localDeclAfterUse;
while (localDeclAfterUse) {
if (Lookup.outerResults().empty()) {
Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name);
Context.Diags.diagnose(innerDecl, diag::decl_declared_here,
localDeclAfterUse);
return errorResult();
}
Lookup.shiftDownResults();
ResultValues.clear();
localDeclAfterUse = nullptr;
AllDeclRefs =
findNonMembers(Lookup.innerResults(), UDRE->getRefKind(),
/*breakOnMember=*/true, ResultValues,
[&](ValueDecl *D) {
return isValidForwardReference(D, DC, &localDeclAfterUse);
});
}
}
}
if (!Lookup) {
// If we failed lookup of an operator, check to see if this is a range
// operator misspelling. Otherwise try to diagnose a juxtaposition
// e.g. (x*-4) that needs whitespace.
if (diagnoseRangeOperatorMisspell(Context.Diags, UDRE) ||
diagnoseIncDecOperator(Context.Diags, UDRE) ||
diagnoseOperatorJuxtaposition(UDRE, DC) ||
diagnoseNonexistentPowerOperator(Context.Diags, UDRE, DC)) {
return errorResult();
}
// Try ignoring access control.
NameLookupOptions relookupOptions = lookupOptions;
relookupOptions |= NameLookupFlags::IgnoreAccessControl;
auto inaccessibleResults =
TypeChecker::lookupUnqualified(DC, LookupName, Loc, relookupOptions);
if (inaccessibleResults) {
// FIXME: What if the unviable candidates have different levels of access?
const ValueDecl *first = inaccessibleResults.front().getValueDecl();
auto accessLevel =
first->getFormalAccessScope().accessLevelForDiagnostics();
Context.Diags.diagnose(Loc, diag::candidate_inaccessible, first,
accessLevel);
// FIXME: If any of the candidates (usually just one) are in the same
// module we could offer a fix-it.
for (auto lookupResult : inaccessibleResults) {
auto *VD = lookupResult.getValueDecl();
VD->diagnose(diag::decl_declared_here, VD);
}
// Don't try to recover here; we'll get more access-related diagnostics
// downstream if the type of the inaccessible decl is also inaccessible.
return errorResult();
}
// Try ignoring missing imports.
relookupOptions |= NameLookupFlags::IgnoreMissingImports;
auto nonImportedResults =
TypeChecker::lookupUnqualified(DC, LookupName, Loc, relookupOptions);
if (nonImportedResults) {
const ValueDecl *first = nonImportedResults.front().getValueDecl();
maybeDiagnoseMissingImportForMember(first, DC, Loc);
// Don't try to recover here; we'll get more access-related diagnostics
// downstream if the type of the inaccessible decl is also inaccessible.
return errorResult();
}
// TODO: Name will be a compound name if it was written explicitly as
// one, but we should also try to propagate labels into this.
DeclNameLoc nameLoc = UDRE->getNameLoc();
Identifier simpleName = Name.getBaseIdentifier();
const char *buffer = simpleName.get();
llvm::SmallString<64> expectedIdentifier;
bool isConfused = false;
uint32_t codepoint;
uint32_t firstConfusableCodepoint = 0;
int totalCodepoints = 0;
int offset = 0;
while ((codepoint = validateUTF8CharacterAndAdvance(buffer,
buffer +
strlen(buffer)))
!= ~0U) {
int length = (buffer - simpleName.get()) - offset;
if (auto expectedCodepoint =
confusable::tryConvertConfusableCharacterToASCII(codepoint)) {
if (firstConfusableCodepoint == 0) {
firstConfusableCodepoint = codepoint;
}
isConfused = true;
expectedIdentifier += expectedCodepoint;
} else {
expectedIdentifier += (char)codepoint;
}
totalCodepoints++;
offset += length;
}
auto emitBasicError = [&] {
if (Name.isSimpleName(Context.Id_self)) {
// `self` gets diagnosed with a different error when it can't be found.
Context.Diags
.diagnose(Loc, diag::cannot_find_self_in_scope)
.highlight(UDRE->getSourceRange());
} else {
Context.Diags
.diagnose(Loc, diag::cannot_find_in_scope, Name,
Name.isOperator())
.highlight(UDRE->getSourceRange());
}
if (!Context.LangOpts.DisableExperimentalClangImporterDiagnostics) {
Context.getClangModuleLoader()->diagnoseTopLevelValue(
Name.getFullName());
}
};
if (!isConfused) {
if (Name.isSimpleName(Context.Id_Self)) {
if (DeclContext *typeContext = DC->getInnermostTypeContext()){
Type SelfType = typeContext->getSelfInterfaceType();
if (typeContext->getSelfClassDecl() &&
!typeContext->getSelfClassDecl()->isForeignReferenceType())
SelfType = DynamicSelfType::get(SelfType, Context);
return new (Context)
TypeExpr(new (Context) SelfTypeRepr(SelfType, Loc));
}
}
TypoCorrectionResults corrections(Name, nameLoc);
// FIXME: Don't perform typo correction inside macro arguments, because it
// will invoke synthesizing declarations in this scope, which will attempt to
// expand this macro which leads to circular reference errors.
if (!namelookup::isInMacroArgument(DC->getParentSourceFile(), UDRE->getLoc())) {
TypeChecker::performTypoCorrection(DC, UDRE->getRefKind(), Type(),
lookupOptions, corrections);
}
if (auto typo = corrections.claimUniqueCorrection()) {
auto diag = Context.Diags.diagnose(
Loc, diag::cannot_find_in_scope_corrected, Name,
Name.isOperator(), typo->CorrectedName.getBaseIdentifier().str());
diag.highlight(UDRE->getSourceRange());
typo->addFixits(diag);
} else {
emitBasicError();
}
corrections.noteAllCandidates();
} else {
emitBasicError();
if (totalCodepoints == 1) {
auto charNames = confusable::getConfusableAndBaseCodepointNames(
firstConfusableCodepoint);
Context.Diags
.diagnose(Loc, diag::single_confusable_character,
UDRE->getName().isOperator(), simpleName.str(),
charNames.first, expectedIdentifier, charNames.second)
.fixItReplace(Loc, expectedIdentifier);
} else {
Context.Diags
.diagnose(Loc, diag::confusable_character,
UDRE->getName().isOperator(), simpleName.str(),
expectedIdentifier)
.fixItReplace(Loc, expectedIdentifier);
}
}
// TODO: consider recovering from here. We may want some way to suppress
// downstream diagnostics, though.
return errorResult();
}
// FIXME: Need to refactor the way we build an AST node from a lookup result!
auto buildTypeExpr = [&](TypeDecl *D) -> Expr * {
// FIXME: This is odd.
if (isa<ModuleDecl>(D)) {
return new (Context) DeclRefExpr(
D, UDRE->getNameLoc(),
/*Implicit=*/false, AccessSemantics::Ordinary, D->getInterfaceType());
}
auto *LookupDC = Lookup[0].getDeclContext();
bool makeTypeValue = false;
if (isa<GenericTypeParamDecl>(D) &&
cast<GenericTypeParamDecl>(D)->isValue()) {
makeTypeValue = true;
}
if (UDRE->isImplicit()) {
return TypeExpr::createImplicitForDecl(
UDRE->getNameLoc(), D, LookupDC,
// It might happen that LookupDC is null if this is checking
// synthesized code, in that case, don't map the type into context,
// but return as is -- the synthesis should ensure the type is
// correct.
LookupDC ? LookupDC->mapTypeIntoContext(D->getInterfaceType())
: D->getInterfaceType());
} else {
if (makeTypeValue) {
return TypeValueExpr::createForDecl(UDRE->getNameLoc(), D, LookupDC);
} else {
return TypeExpr::createForDecl(UDRE->getNameLoc(), D, LookupDC);
}
}
};
// If we have an unambiguous reference to a type decl, form a TypeExpr.
if (Lookup.size() == 1 && UDRE->getRefKind() == DeclRefKind::Ordinary &&
isa<TypeDecl>(Lookup[0].getValueDecl())) {
return buildTypeExpr(cast<TypeDecl>(Lookup[0].getValueDecl()));
}
if (AllDeclRefs) {
// Diagnose uses of operators that found no matching candidates.
if (ResultValues.empty()) {
assert(UDRE->getRefKind() != DeclRefKind::Ordinary);
Context.Diags.diagnose(
Loc, diag::use_nonmatching_operator, Name,
UDRE->getRefKind() == DeclRefKind::BinaryOperator
? 0
: UDRE->getRefKind() == DeclRefKind::PrefixOperator ? 1 : 2);
return errorResult();
}
// For operators, sort the results so that non-generic operations come
// first.
// Note: this is part of a performance hack to prefer non-generic operators
// to generic operators, because the former is far more efficient to check.
if (UDRE->getRefKind() != DeclRefKind::Ordinary) {
std::stable_sort(ResultValues.begin(), ResultValues.end(),
[&](ValueDecl *x, ValueDecl *y) -> bool {
auto xGeneric = x->getInterfaceType()->getAs<GenericFunctionType>();
auto yGeneric = y->getInterfaceType()->getAs<GenericFunctionType>();
if (static_cast<bool>(xGeneric) != static_cast<bool>(yGeneric)) {
return xGeneric? false : true;
}
if (!xGeneric)
return false;
unsigned xDepth = xGeneric->getGenericSignature()->getMaxDepth();
unsigned yDepth = yGeneric->getGenericSignature()->getMaxDepth();
return xDepth < yDepth;
});
}
// Filter out macro declarations without `#` if there are valid
// non-macro results.
if (llvm::any_of(ResultValues,
[](const ValueDecl *D) { return !isa<MacroDecl>(D); })) {
ResultValues.erase(
llvm::remove_if(ResultValues,
[](const ValueDecl *D) { return isa<MacroDecl>(D); }),
ResultValues.end());
// If there is only one type reference in results, let's handle
// this in a special way.
if (ResultValues.size() == 1 &&
UDRE->getRefKind() == DeclRefKind::Ordinary &&
isa<TypeDecl>(ResultValues.front())) {
return buildTypeExpr(cast<TypeDecl>(ResultValues.front()));
}
}
// If we are in an @_unsafeInheritExecutor context, swap out
// declarations for their _unsafeInheritExecutor_ counterparts if they
// exist.
if (enclosingUnsafeInheritsExecutor(DC)) {
introduceUnsafeInheritExecutorReplacements(
DC, UDRE->getNameLoc().getBaseNameLoc(), ResultValues);
}
return buildRefExpr(ResultValues, DC, UDRE->getNameLoc(),
UDRE->isImplicit(), UDRE->getFunctionRefInfo());
}
ResultValues.clear();
bool AllMemberRefs = true;
ValueDecl *Base = nullptr;
DeclContext *BaseDC = nullptr;
for (auto Result : Lookup) {
auto ThisBase = Result.getBaseDecl();
// Track the base for member declarations.
if (ThisBase && !isa<ModuleDecl>(ThisBase)) {
auto Value = Result.getValueDecl();
ResultValues.push_back(Value);
if (Base && ThisBase != Base) {
AllMemberRefs = false;
break;
}
Base = ThisBase;
BaseDC = Result.getDeclContext();
continue;
}
AllMemberRefs = false;
break;
}
if (AllMemberRefs) {
Expr *BaseExpr;
if (auto PD = dyn_cast<ProtocolDecl>(Base)) {
auto selfParam = PD->getGenericParams()->getParams().front();
BaseExpr = TypeExpr::createImplicitForDecl(
UDRE->getNameLoc(), selfParam,
/*DC*/ nullptr,
DC->mapTypeIntoContext(selfParam->getInterfaceType()));
} else if (auto NTD = dyn_cast<NominalTypeDecl>(Base)) {
BaseExpr = TypeExpr::createImplicitForDecl(
UDRE->getNameLoc(), NTD, BaseDC,
DC->mapTypeIntoContext(NTD->getInterfaceType()));
} else {
BaseExpr = new (Context) DeclRefExpr(Base, UDRE->getNameLoc(),
/*Implicit=*/true);
}
auto isInClosureContext = [&](ValueDecl *decl) -> bool {
auto *DC = decl->getDeclContext();
do {
if (dyn_cast<ClosureExpr>(DC))
return true;
} while ((DC = DC->getParent()));
return false;
};
llvm::SmallVector<ValueDecl *, 4> outerAlternatives;
(void)findNonMembers(Lookup.outerResults(), UDRE->getRefKind(),
/*breakOnMember=*/false, outerAlternatives,
/*isValid=*/[&](ValueDecl *choice) -> bool {
// Values that are defined in a closure
// that hasn't been type-checked yet,
// cannot be outer candidates.
if (isInClosureContext(choice)) {
return choice->hasInterfaceType() &&
!choice->isInvalid();
}
return !choice->isInvalid();
});
// Otherwise, form an UnresolvedDotExpr and sema will resolve it based on
// type information.
return new (Context) UnresolvedDotExpr(
BaseExpr, SourceLoc(), Name, UDRE->getNameLoc(), UDRE->isImplicit(),
Context.AllocateCopy(outerAlternatives));
}
// FIXME: If we reach this point, the program we're being handed is likely
// very broken, but it's still conceivable that this may happen due to
// invalid shadowed declarations.
//
// Make sure we emit a diagnostic, since returning an ErrorExpr without
// producing one will break things downstream.
Context.Diags.diagnose(Loc, diag::ambiguous_decl_ref, Name);
for (auto Result : Lookup) {
auto *Decl = Result.getValueDecl();
Context.Diags.diagnose(Decl, diag::decl_declared_here, Decl);
}
return errorResult();
}
/// If an expression references 'self.init' or 'super.init' in an
/// initializer context, returns the implicit 'self' decl of the constructor.
/// Otherwise, return nil.
VarDecl *
TypeChecker::getSelfForInitDelegationInConstructor(DeclContext *DC,
UnresolvedDotExpr *ctorRef) {
// If the reference isn't to a constructor, we're done.
if (!ctorRef->getName().getBaseName().isConstructor())
return nullptr;
if (auto ctorContext =
dyn_cast_or_null<ConstructorDecl>(DC->getInnermostMethodContext())) {
auto nestedArg = ctorRef->getBase();
if (auto inout = dyn_cast<InOutExpr>(nestedArg))
nestedArg = inout->getSubExpr();
if (nestedArg->isSuperExpr())
return ctorContext->getImplicitSelfDecl();
if (auto declRef = dyn_cast<DeclRefExpr>(nestedArg))
if (declRef->getDecl()->getName() == DC->getASTContext().Id_self)
return ctorContext->getImplicitSelfDecl();
}
return nullptr;
}
namespace {
/// Update a direct callee expression node that has a function reference kind
/// based on seeing a call to this callee.
template <typename E, typename = decltype(((E *)nullptr)->getFunctionRefInfo())>
void tryUpdateDirectCalleeImpl(E *callee, int) {
callee->setFunctionRefInfo(
callee->getFunctionRefInfo().addingApplicationLevel());
}
/// Version of tryUpdateDirectCalleeImpl for when the callee
/// expression type doesn't carry a reference.
template <typename E>
void tryUpdateDirectCalleeImpl(E *callee, ...) {}
/// The given expression is the direct callee of a call expression; mark it to
/// indicate that it has been called.
void markDirectCallee(Expr *callee) {
while (true) {
// Look through identity expressions.
if (auto identity = dyn_cast<IdentityExpr>(callee)) {
callee = identity->getSubExpr();
continue;
}
// Look through unresolved 'specialize' expressions.
if (auto specialize = dyn_cast<UnresolvedSpecializeExpr>(callee)) {
callee = specialize->getSubExpr();
continue;
}
// Look through optional binding.
if (auto bindOptional = dyn_cast<BindOptionalExpr>(callee)) {
callee = bindOptional->getSubExpr();
continue;
}
// Look through forced binding.
if (auto force = dyn_cast<ForceValueExpr>(callee)) {
callee = force->getSubExpr();
continue;
}
// Calls compose.
if (auto call = dyn_cast<CallExpr>(callee)) {
callee = call->getFn();
continue;
}
// We're done.
break;
}
// Cast the callee to its most-specific class, then try to perform an
// update. If the expression node has a declaration reference in it, the
// update will succeed. Otherwise, we're done propagating.
switch (callee->getKind()) {
#define EXPR(Id, Parent) \
case ExprKind::Id: \
tryUpdateDirectCalleeImpl(cast<Id##Expr>(callee), 0); \
break;
#include "swift/AST/ExprNodes.def"
}
}
class PreCheckTarget final : public ASTWalker {
ASTContext &Ctx;
DeclContext *DC;
/// A stack of expressions being walked, used to determine where to
/// insert RebindSelfInConstructorExpr nodes.
llvm::SmallVector<Expr *, 8> ExprStack;
/// The 'self' variable to use when rebinding 'self' in a constructor.
VarDecl *UnresolvedCtorSelf = nullptr;
/// The expression that will be wrapped by a RebindSelfInConstructorExpr
/// node when visited.
Expr *UnresolvedCtorRebindTarget = nullptr;
/// Keep track of acceptable DiscardAssignmentExpr's.
llvm::SmallPtrSet<DiscardAssignmentExpr *, 2> CorrectDiscardAssignmentExprs;
/// Keep track of any out-of-place SingleValueStmtExprs. We populate this as
/// we encounter SingleValueStmtExprs, and erase them as we walk up to a
/// valid parent in the post walk.
llvm::SetVector<SingleValueStmtExpr *> OutOfPlaceSingleValueStmtExprs;
/// Simplify expressions which are type sugar productions that got parsed
/// as expressions due to the parser not knowing which identifiers are
/// type names.
TypeExpr *simplifyTypeExpr(Expr *E);
/// Simplify unresolved dot expressions which are nested type productions.
TypeExpr *simplifyNestedTypeExpr(UnresolvedDotExpr *UDE);
TypeExpr *simplifyUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *USE);
/// Simplify a key path expression into a canonical form.
void resolveKeyPathExpr(KeyPathExpr *KPE);
/// Simplify constructs like `UInt32(1)` into `1 as UInt32` if
/// the type conforms to the expected literal protocol.
///
/// \returns Either a transformed expression, or `ErrorExpr` upon type
/// resolution failure, or `nullptr` if transformation is not applicable.
Expr *simplifyTypeConstructionWithLiteralArg(Expr *E);
/// Pull some operator expressions into the optional chain.
OptionalEvaluationExpr *hoistOptionalEvaluationExprIfNeeded(Expr *E);
/// Wrap an unresolved member or optional bind chain in an
/// UnresolvedMemberChainResultExpr or OptionalEvaluationExpr respectively.
Expr *wrapMemberChainIfNeeded(Expr *E);
/// Whether the given expression "looks like" a (possibly sugared) type. For
/// example, `(foo, bar)` "looks like" a type, but `foo + bar` does not.
bool exprLooksLikeAType(Expr *expr);
/// Whether the current expression \p E is in a context that might turn out
/// to be a \c TypeExpr after \c simplifyTypeExpr is called up the tree.
/// This function allows us to make better guesses about whether invalid
/// uses of '_' were "supposed" to be \c DiscardAssignmentExprs or patterns,
/// which results in better diagnostics after type checking.
bool possiblyInTypeContext(Expr *E);
/// Whether we can simplify the given discard assignment expr. Not possible
/// if it's been marked "valid" or if the current state of the AST disallows
/// such simplification (see \c canSimplifyPlaceholderTypes above).
bool canSimplifyDiscardAssignmentExpr(DiscardAssignmentExpr *DAE);
/// In Swift < 5, diagnose and correct invalid multi-argument or
/// argument-labeled interpolations. Returns \c true if the AST walk should
/// continue, or \c false if it should be aborted.
bool correctInterpolationIfStrange(InterpolatedStringLiteralExpr *ISLE);
/// Scout out the specified destination of an AssignExpr to recursively
/// identify DiscardAssignmentExpr in legal places. We can only allow them
/// in simple pattern-like expressions, so we reject anything complex here.
void markAcceptableDiscardExprs(Expr *E);
/// Check and diagnose an invalid SingleValueStmtExpr.
void checkSingleValueStmtExpr(SingleValueStmtExpr *SVE);
/// Diagnose any SingleValueStmtExprs in an unsupported position.
void
diagnoseOutOfPlaceSingleValueStmtExprs(const SyntacticElementTarget &target);
/// Mark a given expression as a valid position for a SingleValueStmtExpr.
void markValidSingleValueStmt(Expr *E);
/// For the given expr, mark any valid SingleValueStmtExpr children.
void markAnyValidSingleValueStmts(Expr *E);
/// For the given statement, mark any valid SingleValueStmtExpr children.
void markAnyValidSingleValueStmts(Stmt *S);
PreCheckTarget(DeclContext *dc) : Ctx(dc->getASTContext()), DC(dc) {}
public:
static std::optional<SyntacticElementTarget>
check(const SyntacticElementTarget &target) {
PreCheckTarget checker(target.getDeclContext());
auto newTarget = target.walk(checker);
if (!newTarget)
return std::nullopt;
// Diagnose any remaining out-of-place SingleValueStmtExprs.
checker.diagnoseOutOfPlaceSingleValueStmtExprs(*newTarget);
return *newTarget;
}
ASTContext &getASTContext() const { return Ctx; }
bool walkToClosureExprPre(ClosureExpr *expr, ParentTy &parent);
MacroWalking getMacroWalkingBehavior() const override {
return MacroWalking::Arguments;
}
VarDecl *getImplicitSelfDeclForSuperContext(SourceLoc Loc);
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
auto &diags = Ctx.Diags;
// Fold sequence expressions.
if (auto *seqExpr = dyn_cast<SequenceExpr>(expr)) {
auto result = TypeChecker::foldSequence(seqExpr, DC);
result = result->walk(*this);
if (!result)
return Action::Stop();
// Already walked.
return Action::SkipNode(result);
}
// FIXME(diagnostics): `InOutType` could appear here as a result
// of successful re-typecheck of the one of the sub-expressions e.g.
// `let _: Int = { (s: inout S) in s.bar() }`. On the first
// attempt to type-check whole expression `s.bar()` - is going
// to have a base which points directly to declaration of `S`.
// But when diagnostics attempts to type-check `s.bar()` standalone
// its base would be transformed into `InOutExpr -> DeclRefExr`,
// and `InOutType` is going to be recorded in constraint system.
// One possible way to fix this (if diagnostics still use typecheck)
// might be to make it so self is not wrapped into `InOutExpr`
// but instead used as @lvalue type in some case of mutable members.
if (!expr->isImplicit()) {
if (isa<MemberRefExpr>(expr) || isa<DynamicMemberRefExpr>(expr)) {
auto *LE = cast<LookupExpr>(expr);
if (auto *IOE = dyn_cast<InOutExpr>(LE->getBase()))
LE->setBase(IOE->getSubExpr());
}
if (auto *DSCE = dyn_cast<DotSyntaxCallExpr>(expr)) {
if (auto *IOE = dyn_cast<InOutExpr>(DSCE->getBase()))
DSCE->setBase(IOE->getSubExpr());
}
}
// Local function used to finish up processing before returning. Every
// return site should call through here.
auto finish = [&](bool recursive, Expr *expr) -> PreWalkResult<Expr *> {
if (!expr)
return Action::Stop();
// If we're going to recurse, record this expression on the stack.
if (recursive)
ExprStack.push_back(expr);
return Action::VisitNodeIf(recursive, expr);
};
// Resolve 'super' references.
if (auto *superRef = dyn_cast<SuperRefExpr>(expr)) {
auto loc = superRef->getLoc();
auto *selfDecl = getImplicitSelfDeclForSuperContext(loc);
if (selfDecl == nullptr)
return finish(false, new (Ctx) ErrorExpr(loc));
superRef->setSelf(selfDecl);
const bool isValidSuper = [&]() -> bool {
auto *parentExpr = Parent.getAsExpr();
if (!parentExpr) {
return false;
}
if (isa<UnresolvedDotExpr>(parentExpr) ||
isa<MemberRefExpr>(parentExpr)) {
return true;
} else if (auto *SE = dyn_cast<SubscriptExpr>(parentExpr)) {
// 'super[]' is valid, but 'x[super]' is not.
return superRef == SE->getBase();
}
return false;
}();
// NB: This is done along the happy path because presenting this error
// in a context where 'super' is not legal to begin with is not helpful.
if (!isValidSuper) {
// Diagnose and keep going. It is important for source tooling such
// as code completion that Sema is able to provide type information
// for 'super' in arbitrary positions inside expressions.
diags.diagnose(loc, diag::super_invalid_parent_expr);
}
return finish(true, superRef);
}
// For closures, type-check the patterns and result type as written,
// but do not walk into the body. That will be type-checked after
// we've determine the complete function type.
if (auto closure = dyn_cast<ClosureExpr>(expr))
return finish(walkToClosureExprPre(closure, Parent), expr);
if (auto *unresolved = dyn_cast<UnresolvedDeclRefExpr>(expr))
return finish(true, TypeChecker::resolveDeclRefExpr(unresolved, DC));
// Let's try to figure out if `InOutExpr` is out of place early
// otherwise there is a risk of producing solutions which can't
// be later applied to AST and would result in the crash in some
// cases. Such expressions are only allowed in argument positions
// of function/operator calls.
if (isa<InOutExpr>(expr)) {
// If this is an implicit `inout` expression we assume that
// compiler knowns what it's doing.
if (expr->isImplicit())
return finish(true, expr);
ArrayRef<Expr *> parents = ExprStack;
auto takeNextParent = [&]() -> Expr * {
if (parents.empty())
return nullptr;
auto parent = parents.back();
parents = parents.drop_back();
return parent;
};
if (auto *parent = takeNextParent()) {
SourceLoc lastInnerParenLoc;
// Unwrap to the outermost paren in the sequence.
// e.g. `foo(((&bar))`
while (auto *PE = dyn_cast<ParenExpr>(parent)) {
auto nextParent = takeNextParent();
if (!nextParent)
break;
lastInnerParenLoc = PE->getLParenLoc();
parent = nextParent;
}
if (isa<ApplyExpr>(parent) || isa<UnresolvedMemberExpr>(parent)) {
// If outermost paren is associated with a call or
// a member reference, it might be valid to have `&`
// before all of the parens.
if (lastInnerParenLoc.isValid()) {
auto diag = diags.diagnose(expr->getStartLoc(),
diag::extraneous_address_of);
diag.fixItExchange(expr->getLoc(), lastInnerParenLoc);
}
if (!parents.empty() && isa<KeyPathExpr>(parents[0]))
diags.diagnose(expr->getStartLoc(),
diag::cannot_pass_inout_arg_to_keypath_method);
return finish(true, expr);
}
if (isa<SubscriptExpr>(parent)) {
diags.diagnose(expr->getStartLoc(),
diag::cannot_pass_inout_arg_to_subscript);
return finish(false, nullptr);
}
}
diags.diagnose(expr->getStartLoc(), diag::extraneous_address_of);
return finish(false, nullptr);
}
if (auto *ISLE = dyn_cast<InterpolatedStringLiteralExpr>(expr)) {
if (!correctInterpolationIfStrange(ISLE))
return finish(false, nullptr);
}
if (auto *assignment = dyn_cast<AssignExpr>(expr))
markAcceptableDiscardExprs(assignment->getDest());
if (auto *SVE = dyn_cast<SingleValueStmtExpr>(expr))
checkSingleValueStmtExpr(SVE);
return finish(true, expr);
}
PostWalkResult<Expr *> walkToExprPost(Expr *expr) override {
// Remove this expression from the stack.
assert(ExprStack.back() == expr);
ExprStack.pop_back();
// Mark any valid SingleValueStmtExpr children.
markAnyValidSingleValueStmts(expr);
// Type check the type parameters in an UnresolvedSpecializeExpr.
if (auto *us = dyn_cast<UnresolvedSpecializeExpr>(expr)) {
if (auto *typeExpr = simplifyUnresolvedSpecializeExpr(us))
return Action::Continue(typeExpr);
}
// Check whether this is standalone `self` in init accessor, which
// is invalid.
if (auto *DRE = dyn_cast<DeclRefExpr>(expr)) {
if (auto *accessor = DC->getInnermostPropertyAccessorContext()) {
if (accessor->isInitAccessor() &&
accessor->getImplicitSelfDecl() == DRE->getDecl() &&
!isa_and_nonnull<UnresolvedDotExpr>(Parent.getAsExpr())) {
Ctx.Diags.diagnose(DRE->getLoc(),
diag::invalid_use_of_self_in_init_accessor);
return Action::Continue(new (Ctx) ErrorExpr(DRE->getSourceRange()));
}
}
}
// If we're about to step out of a ClosureExpr, restore the DeclContext.
if (auto *ce = dyn_cast<ClosureExpr>(expr)) {
assert(DC == ce && "DeclContext imbalance");
DC = ce->getParent();
}
if (auto *apply = dyn_cast<ApplyExpr>(expr)) {
// Mark the direct callee as being a callee.
markDirectCallee(apply->getFn());
// A 'self.init' or 'super.init' application inside a constructor will
// evaluate to void, with the initializer's result implicitly rebound
// to 'self'. Recognize the unresolved constructor expression and
// determine where to place the RebindSelfInConstructorExpr node.
//
// When updating this logic, also may need to also update
// RebindSelfInConstructorExpr::getCalledConstructor.
VarDecl *self = nullptr;
if (auto *unresolvedDot =
dyn_cast<UnresolvedDotExpr>(apply->getSemanticFn())) {
self = TypeChecker::getSelfForInitDelegationInConstructor(
DC, unresolvedDot);
}
if (self) {
// Walk our ancestor expressions looking for the appropriate place
// to insert the RebindSelfInConstructorExpr.
Expr *target = apply;
for (auto ancestor : llvm::reverse(ExprStack)) {
if (isa<IdentityExpr>(ancestor) || isa<ForceValueExpr>(ancestor) ||
isa<AnyTryExpr>(ancestor)) {
target = ancestor;
continue;
}
if (isa<RebindSelfInConstructorExpr>(ancestor)) {
// If we already have a rebind, then we're re-typechecking an
// expression and are done.
target = nullptr;
}
// No other expression kinds are permitted.
break;
}
// If we found a rebind target, note the insertion point.
if (target) {
UnresolvedCtorRebindTarget = target;
UnresolvedCtorSelf = self;
}
}
}
auto &ctx = getASTContext();
// If the expression we've found is the intended target of an
// RebindSelfInConstructorExpr, wrap it in the
// RebindSelfInConstructorExpr.
if (expr == UnresolvedCtorRebindTarget) {
expr = new (ctx) RebindSelfInConstructorExpr(expr, UnresolvedCtorSelf);
UnresolvedCtorRebindTarget = nullptr;
return Action::Continue(expr);
}
// Check if there are any BindOptionalExpr in the tree which
// wrap DiscardAssignmentExpr, such situation corresponds to syntax
// like - `_? = <value>`, since it doesn't really make
// sense to have optional assignment to discarded LValue which can
// never be optional, we can remove BOE from the tree and avoid
// generating any of the unnecessary constraints.
if (auto BOE = dyn_cast<BindOptionalExpr>(expr)) {
if (auto DAE = dyn_cast<DiscardAssignmentExpr>(BOE->getSubExpr()))
if (CorrectDiscardAssignmentExprs.count(DAE))
return Action::Continue(DAE);
}
// If this is a sugared type that needs to be folded into a single
// TypeExpr, do it.
if (auto *simplified = simplifyTypeExpr(expr))
return Action::Continue(simplified);
// Diagnose a '_' that isn't on the immediate LHS of an assignment. We
// skip diagnostics if we've explicitly marked the expression as valid.
if (auto *DAE = dyn_cast<DiscardAssignmentExpr>(expr)) {
if (!CorrectDiscardAssignmentExprs.count(DAE)) {
ctx.Diags.diagnose(expr->getLoc(),
diag::discard_expr_outside_of_assignment);
return Action::Stop();
}
}
if (auto KPE = dyn_cast<KeyPathExpr>(expr)) {
resolveKeyPathExpr(KPE);
return Action::Continue(KPE);
}
if (auto *result = simplifyTypeConstructionWithLiteralArg(expr)) {
if (isa<ErrorExpr>(result))
return Action::Stop();
return Action::Continue(result);
}
if (auto *OEE = hoistOptionalEvaluationExprIfNeeded(expr)) {
return Action::Continue(OEE);
}
expr = wrapMemberChainIfNeeded(expr);
return Action::Continue(expr);
}
PreWalkResult<Stmt *> walkToStmtPre(Stmt *stmt) override {
if (auto *RS = dyn_cast<ReturnStmt>(stmt)) {
// Pre-check a return statement, which includes potentially turning it
// into a FailStmt.
auto &eval = Ctx.evaluator;
auto *S =
evaluateOrDefault(eval, PreCheckReturnStmtRequest{RS, DC}, nullptr);
if (!S)
return Action::Stop();
return Action::Continue(S);
}
return Action::Continue(stmt);
}
PostWalkResult<Stmt *> walkToStmtPost(Stmt *S) override {
markAnyValidSingleValueStmts(S);
return Action::Continue(S);
}
PreWalkAction walkToDeclPre(Decl *D) override {
return Action::VisitChildrenIf(isa<PatternBindingDecl>(D));
}
PostWalkAction walkToDeclPost(Decl *D) override {
// Mark any valid SingleValueStmtExprs for initializations.
if (auto *PBD = dyn_cast<PatternBindingDecl>(D)) {
for (auto idx : range(PBD->getNumPatternEntries()))
markValidSingleValueStmt(PBD->getInit(idx));
}
return Action::Continue();
}
PreWalkResult<Pattern *> walkToPatternPre(Pattern *pattern) override {
// In general we can't walk into patterns due to the fact that we don't
// currently resolve patterns until constraint generation, and therefore
// shouldn't walk into any expressions that may turn into patterns.
// One exception to this is if the parent is an expression. In that case,
// we are type-checking an expression in an ExprPattern, meaning that
// the pattern will already be resolved, and that we ought to e.g
// diagnose any stray '_' expressions nested within it. This then also
// means we should walk into any child pattern if we walked into the
// parent pattern.
return Action::VisitNodeIf(Parent.getAsExpr() || Parent.getAsPattern(),
pattern);
}
};
} // end anonymous namespace
/// Perform prechecking of a ClosureExpr before we dive into it. This returns
/// true when we want the body to be considered part of this larger expression.
bool PreCheckTarget::walkToClosureExprPre(ClosureExpr *closure,
ParentTy &parent) {
if (auto *expandedBody = closure->getExpandedBody()) {
if (Parent.getAsExpr()) {
// We cannot simply replace the body when closure i.e. is passed
// as an argument to a call or is a source of an assignment
// because the source range of the argument list would cross
// buffer boundaries. One way to avoid that is to inject
// elements into a new implicit brace statement with the original
// source locations. Brace statement has to be implicit because its
// elements are in a different buffer.
auto sourceRange = closure->getSourceRange();
closure->setBody(BraceStmt::create(getASTContext(), sourceRange.Start,
expandedBody->getElements(),
sourceRange.End,
/*implicit=*/true));
} else {
closure->setBody(expandedBody);
}
}
// Pre-check the closure body.
(void)evaluateOrDefault(Ctx.evaluator, PreCheckClosureBodyRequest{closure},
nullptr);
// Update the current DeclContext to be the closure we're about to
// recurse into.
assert((closure->getParent() == DC ||
closure->getParent()->isChildContextOf(DC)) &&
"Decl context isn't correct");
DC = closure;
return true;
}
void PreCheckTarget::markValidSingleValueStmt(Expr *E) {
if (!E)
return;
if (auto *SVE = SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(E))
OutOfPlaceSingleValueStmtExprs.remove(SVE);
}
void PreCheckTarget::checkSingleValueStmtExpr(SingleValueStmtExpr *SVE) {
// We add all SingleValueStmtExprs we see to the out-of-place list, then
// erase them as we walk up to valid parents. We do this instead of populating
// valid positions during the pre-walk to ensure we're looking at the AST
// after e.g folding SequenceExprs.
OutOfPlaceSingleValueStmtExprs.insert(SVE);
// Diagnose invalid SingleValueStmtExprs. This should only happen for
// expressions in positions that we didn't support prior to their introduction
// (e.g assignment or *explicit* return).
auto &Diags = Ctx.Diags;
auto *S = SVE->getStmt();
auto mayProduceSingleValue = S->mayProduceSingleValue(Ctx);
switch (mayProduceSingleValue.getKind()) {
case IsSingleValueStmtResult::Kind::Valid:
break;
case IsSingleValueStmtResult::Kind::UnterminatedBranches: {
for (auto *branch : mayProduceSingleValue.getUnterminatedBranches()) {
if (auto *BS = dyn_cast<BraceStmt>(branch)) {
if (BS->empty()) {
Diags.diagnose(branch->getStartLoc(),
diag::single_value_stmt_branch_empty, S->getKind());
continue;
}
}
// TODO: The wording of this diagnostic will need tweaking if either
// implicit last expressions or 'then' statements are enabled by
// default.
Diags.diagnose(branch->getEndLoc(),
diag::single_value_stmt_branch_must_end_in_result,
S->getKind(), isa<SwitchStmt>(S));
}
break;
}
case IsSingleValueStmtResult::Kind::NonExhaustiveIf: {
Diags.diagnose(S->getStartLoc(),
diag::if_expr_must_be_syntactically_exhaustive);
break;
}
case IsSingleValueStmtResult::Kind::NonExhaustiveDoCatch: {
Diags.diagnose(S->getStartLoc(),
diag::do_catch_expr_must_be_syntactically_exhaustive);
break;
}
case IsSingleValueStmtResult::Kind::HasLabel: {
// FIXME: We should offer a fix-it to remove (currently we don't track
// the colon SourceLoc).
auto label = cast<LabeledStmt>(S)->getLabelInfo();
Diags.diagnose(label.Loc,
diag::single_value_stmt_must_be_unlabeled, S->getKind())
.highlight(label.Loc);
break;
}
case IsSingleValueStmtResult::Kind::InvalidJumps: {
// Diagnose each invalid jump.
for (auto *jump : mayProduceSingleValue.getInvalidJumps()) {
Diags.diagnose(jump->getStartLoc(),
diag::cannot_jump_in_single_value_stmt,
jump->getKind(), S->getKind())
.highlight(jump->getSourceRange());
}
break;
}
case IsSingleValueStmtResult::Kind::NoResult:
// This is fine, we will have typed the expression as Void (we verify
// as such in the ASTVerifier).
break;
case IsSingleValueStmtResult::Kind::CircularReference:
// Already diagnosed.
break;
case IsSingleValueStmtResult::Kind::UnhandledStmt:
break;
}
}
void PreCheckTarget::markAnyValidSingleValueStmts(Expr *E) {
auto findAssignment = [&]() -> AssignExpr * {
// Don't consider assignments if we have a parent expression (as otherwise
// this would be effectively allowing it in an arbitrary expression
// position).
if (Parent.getAsExpr())
return nullptr;
// Look through optional exprs, which are present for e.g x?.y = z, as
// we wrap the entire assign in the optional evaluation of the destination.
if (auto *OEE = dyn_cast<OptionalEvaluationExpr>(E)) {
E = OEE->getSubExpr();
while (auto *IIO = dyn_cast<InjectIntoOptionalExpr>(E))
E = IIO->getSubExpr();
}
// Look through "unsafe" expressions.
if (auto UE = dyn_cast<UnsafeExpr>(E))
E = UE->getSubExpr();
return dyn_cast<AssignExpr>(E);
};
if (auto *AE = findAssignment())
markValidSingleValueStmt(AE->getSrc());
}
void PreCheckTarget::markAnyValidSingleValueStmts(Stmt *S) {
// Valid in a return/throw/then.
if (auto *RS = dyn_cast<ReturnStmt>(S)) {
if (RS->hasResult())
markValidSingleValueStmt(RS->getResult());
}
if (auto *TS = dyn_cast<ThrowStmt>(S))
markValidSingleValueStmt(TS->getSubExpr());
if (auto *TS = dyn_cast<ThenStmt>(S))
markValidSingleValueStmt(TS->getResult());
}
void PreCheckTarget::diagnoseOutOfPlaceSingleValueStmtExprs(
const SyntacticElementTarget &target) {
// Top-level SingleValueStmtExprs are allowed in returns, throws, and
// bindings.
if (auto *E = target.getAsExpr()) {
switch (target.getExprContextualTypePurpose()) {
case CTP_ReturnStmt:
case CTP_ThrowStmt:
case CTP_Initialization:
markValidSingleValueStmt(E);
break;
default:
break;
}
}
for (auto *SVE : OutOfPlaceSingleValueStmtExprs) {
Ctx.Diags.diagnose(SVE->getLoc(), diag::single_value_stmt_out_of_place,
SVE->getStmt()->getKind());
}
}
TypeExpr *PreCheckTarget::simplifyNestedTypeExpr(UnresolvedDotExpr *UDE) {
if (!UDE->getName().isSimpleName() ||
UDE->getName().isSpecial())
return nullptr;
auto Name = UDE->getName();
auto NameLoc = UDE->getNameLoc().getBaseNameLoc();
// Qualified type lookup with a module base is represented as a DeclRefExpr
// and not a TypeExpr.
auto handleNestedTypeLookup = [&](
TypeDecl *TD, DeclNameLoc ParentNameLoc
) -> TypeExpr * {
// See if the type has a member type with this name.
auto Result = TypeChecker::lookupMemberType(
DC, TD->getDeclaredInterfaceType(), Name,
UDE->getLoc(), defaultMemberLookupOptions);
// If there is no nested type with this name, we have a lookup of
// a non-type member, so leave the expression as-is.
if (Result.size() == 1) {
return TypeExpr::createForMemberDecl(
ParentNameLoc, TD, UDE->getNameLoc(), Result.front().Member);
}
return nullptr;
};
if (auto *DRE = dyn_cast<DeclRefExpr>(UDE->getBase())) {
if (auto *TD = dyn_cast<TypeDecl>(DRE->getDecl()))
return handleNestedTypeLookup(TD, DRE->getNameLoc());
return nullptr;
}
// Determine whether there is exactly one type declaration, where all
// other declarations are macros.
if (auto *ODRE = dyn_cast<OverloadedDeclRefExpr>(UDE->getBase())) {
TypeDecl *FoundTD = nullptr;
for (auto *D : ODRE->getDecls()) {
if (auto *TD = dyn_cast<TypeDecl>(D)) {
if (FoundTD)
return nullptr;
FoundTD = TD;
continue;
}
// Ignore macros; they can't have any nesting.
if (isa<MacroDecl>(D))
continue;
// Anything else prevents folding.
return nullptr;
}
if (FoundTD)
return handleNestedTypeLookup(FoundTD, ODRE->getNameLoc());
return nullptr;
}
auto *TyExpr = dyn_cast<TypeExpr>(UDE->getBase());
if (!TyExpr)
return nullptr;
auto *InnerTypeRepr = TyExpr->getTypeRepr();
if (!InnerTypeRepr)
return nullptr;
// Fold 'T.Protocol' into a protocol metatype.
if (Name.isSimpleName(getASTContext().Id_Protocol)) {
auto *NewTypeRepr =
new (getASTContext()) ProtocolTypeRepr(InnerTypeRepr, NameLoc);
return new (getASTContext()) TypeExpr(NewTypeRepr);
}
// Fold 'T.Type' into an existential metatype if 'T' is a protocol,
// or an ordinary metatype otherwise.
if (Name.isSimpleName(getASTContext().Id_Type)) {
auto *NewTypeRepr =
new (getASTContext()) MetatypeTypeRepr(InnerTypeRepr, NameLoc);
return new (getASTContext()) TypeExpr(NewTypeRepr);
}
// Fold 'T.U' into a nested type.
// Resolve the TypeRepr to get the base type for the lookup.
TypeResolutionOptions options(TypeResolverContext::InExpression);
// Pre-check always allows pack references during TypeExpr folding.
// CSGen will diagnose cases that appear outside of pack expansion
// expressions.
options |= TypeResolutionFlags::AllowPackReferences;
const auto BaseTy = TypeResolution::resolveContextualType(
InnerTypeRepr, DC, options,
[](auto unboundTy) {
// FIXME: Don't let unbound generic types escape type resolution.
// For now, just return the unbound generic type.
return unboundTy;
},
// FIXME: Don't let placeholder types escape type resolution.
// For now, just return the placeholder type.
PlaceholderType::get,
// TypeExpr pack elements are opened in CSGen.
/*packElementOpener*/ nullptr);
if (BaseTy->mayHaveMembers()) {
// See if there is a member type with this name.
auto Result = TypeChecker::lookupMemberType(DC, BaseTy, Name,
UDE->getLoc(),
defaultMemberLookupOptions);
// If there is no nested type with this name, we have a lookup of
// a non-type member, so leave the expression as-is.
if (Result.size() == 1) {
return TypeExpr::createForMemberDecl(InnerTypeRepr, UDE->getNameLoc(),
Result.front().Member);
}
}
return nullptr;
}
TypeExpr *PreCheckTarget::simplifyUnresolvedSpecializeExpr(
UnresolvedSpecializeExpr *us) {
// If this is a reference type a specialized type, form a TypeExpr.
// The base should be a TypeExpr that we already resolved.
if (auto *te = dyn_cast_or_null<TypeExpr>(us->getSubExpr())) {
if (auto *declRefTR =
dyn_cast_or_null<DeclRefTypeRepr>(te->getTypeRepr())) {
return TypeExpr::createForSpecializedDecl(
declRefTR, us->getUnresolvedParams(),
SourceRange(us->getLAngleLoc(), us->getRAngleLoc()), getASTContext());
}
}
return nullptr;
}
/// Whether the given expression "looks like" a (possibly sugared) type. For
/// example, `(foo, bar)` "looks like" a type, but `foo + bar` does not.
bool PreCheckTarget::exprLooksLikeAType(Expr *expr) {
return isa<OptionalEvaluationExpr>(expr) ||
isa<BindOptionalExpr>(expr) ||
isa<ForceValueExpr>(expr) ||
isa<ParenExpr>(expr) ||
isa<ArrowExpr>(expr) ||
isa<PackExpansionExpr>(expr) ||
isa<PackElementExpr>(expr) ||
isa<TupleExpr>(expr) ||
(isa<ArrayExpr>(expr) &&
cast<ArrayExpr>(expr)->getElements().size() == 1) ||
(isa<DictionaryExpr>(expr) &&
cast<DictionaryExpr>(expr)->getElements().size() == 1) ||
getCompositionExpr(expr);
}
bool PreCheckTarget::possiblyInTypeContext(Expr *E) {
// Walk back up the stack of parents looking for a valid type context.
for (auto *ParentExpr : llvm::reverse(ExprStack)) {
// We're considered to be in a type context if either:
// - We have a valid parent for a TypeExpr, or
// - The parent "looks like" a type (and is not a call arg), and we can
// reach a valid parent for a TypeExpr if we continue walking.
if (ParentExpr->isValidParentOfTypeExpr(E))
return true;
if (!exprLooksLikeAType(ParentExpr))
return false;
E = ParentExpr;
}
return false;
}
/// Only allow simplification of a DiscardAssignmentExpr if it hasn't already
/// been explicitly marked as correct, and the current AST state allows it.
bool PreCheckTarget::canSimplifyDiscardAssignmentExpr(
DiscardAssignmentExpr *DAE) {
return !CorrectDiscardAssignmentExprs.count(DAE) &&
possiblyInTypeContext(DAE);
}
/// In Swift < 5, diagnose and correct invalid multi-argument or
/// argument-labeled interpolations. Returns \c true if the AST walk should
/// continue, or \c false if it should be aborted.
bool PreCheckTarget::correctInterpolationIfStrange(
InterpolatedStringLiteralExpr *ISLE) {
// These expressions are valid in Swift 5+.
if (getASTContext().isSwiftVersionAtLeast(5))
return true;
/// Diagnoses appendInterpolation(...) calls with multiple
/// arguments or argument labels and corrects them.
class StrangeInterpolationRewriter : public ASTWalker {
ASTContext &Context;
public:
StrangeInterpolationRewriter(ASTContext &Ctx) : Context(Ctx) {}
MacroWalking getMacroWalkingBehavior() const override {
return MacroWalking::Expansion;
}
virtual PreWalkAction walkToDeclPre(Decl *D) override {
// We don't want to look inside decls.
return Action::SkipNode();
}
virtual PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
// One InterpolatedStringLiteralExpr should never be nested inside
// another except as a child of a CallExpr, and we don't recurse into
// the children of CallExprs.
assert(!isa<InterpolatedStringLiteralExpr>(E) &&
"StrangeInterpolationRewriter found nested interpolation?");
// We only care about CallExprs.
if (!isa<CallExpr>(E))
return Action::Continue(E);
auto *call = cast<CallExpr>(E);
auto *args = call->getArgs();
auto lParen = args->getLParenLoc();
auto rParen = args->getRParenLoc();
if (auto callee = dyn_cast<UnresolvedDotExpr>(call->getFn())) {
if (callee->getName().getBaseName() ==
Context.Id_appendInterpolation) {
std::optional<Argument> newArg;
if (args->size() > 1) {
auto *secondArg = args->get(1).getExpr();
Context.Diags
.diagnose(secondArg->getLoc(),
diag::string_interpolation_list_changing)
.highlightChars(secondArg->getLoc(), rParen);
Context.Diags
.diagnose(secondArg->getLoc(),
diag::string_interpolation_list_insert_parens)
.fixItInsertAfter(lParen, "(")
.fixItInsert(rParen, ")");
// Make sure we don't have an inout arg somewhere, as that's
// invalid even with the compatibility fix.
for (auto arg : *args) {
if (arg.isInOut()) {
Context.Diags.diagnose(arg.getExpr()->getStartLoc(),
diag::extraneous_address_of);
return Action::Stop();
}
}
// Form a new argument tuple from the argument list.
auto *packed = args->packIntoImplicitTupleOrParen(Context);
newArg = Argument::unlabeled(packed);
} else if (args->size() == 1 &&
args->front().getLabel() != Identifier()) {
// Form a new argument that drops the label.
auto *argExpr = args->front().getExpr();
newArg = Argument::unlabeled(argExpr);
SourceLoc argLabelLoc = args->front().getLabelLoc(),
argLoc = argExpr->getStartLoc();
Context.Diags
.diagnose(argLabelLoc,
diag::string_interpolation_label_changing)
.highlightChars(argLabelLoc, argLoc);
Context.Diags
.diagnose(argLabelLoc,
diag::string_interpolation_remove_label,
args->front().getLabel())
.fixItRemoveChars(argLabelLoc, argLoc);
}
// If newArg is no longer null, we need to build a new
// appendInterpolation(_:) call that takes it to replace the bad
// appendInterpolation(...) call.
if (newArg) {
auto newCallee = new (Context) UnresolvedDotExpr(
callee->getBase(), /*dotloc=*/SourceLoc(),
DeclNameRef(Context.Id_appendInterpolation),
/*nameloc=*/DeclNameLoc(), /*Implicit=*/true);
auto *newArgList =
ArgumentList::create(Context, lParen, {*newArg}, rParen,
/*trailingClosureIdx*/ std::nullopt,
/*implicit*/ false);
E = CallExpr::create(Context, newCallee, newArgList,
/*implicit=*/false);
}
}
}
// There is never a CallExpr between an InterpolatedStringLiteralExpr
// and an un-typechecked appendInterpolation(...) call, so whether we
// changed E or not, we don't need to recurse any deeper.
return Action::SkipNode(E);
}
};
return ISLE->getAppendingExpr()->walk(
StrangeInterpolationRewriter(getASTContext()));
}
/// Scout out the specified destination of an AssignExpr to recursively
/// identify DiscardAssignmentExpr in legal places. We can only allow them
/// in simple pattern-like expressions, so we reject anything complex here.
void PreCheckTarget::markAcceptableDiscardExprs(Expr *E) {
if (!E) return;
if (auto *PE = dyn_cast<ParenExpr>(E))
return markAcceptableDiscardExprs(PE->getSubExpr());
if (auto *TE = dyn_cast<TupleExpr>(E)) {
for (auto &elt : TE->getElements())
markAcceptableDiscardExprs(elt);
return;
}
if (auto *BOE = dyn_cast<BindOptionalExpr>(E))
return markAcceptableDiscardExprs(BOE->getSubExpr());
if (auto *DAE = dyn_cast<DiscardAssignmentExpr>(E))
CorrectDiscardAssignmentExprs.insert(DAE);
// Otherwise, we can't support this.
}
VarDecl *PreCheckTarget::getImplicitSelfDeclForSuperContext(SourceLoc Loc) {
auto *methodContext = DC->getInnermostMethodContext();
if (auto *typeContext = DC->getInnermostTypeContext()) {
auto *nominal = typeContext->getSelfNominalTypeDecl();
auto *classDecl = dyn_cast<ClassDecl>(nominal);
if (!classDecl) {
Ctx.Diags.diagnose(Loc, diag::super_in_nonclass_type, nominal);
return nullptr;
} else if (!methodContext) {
Ctx.Diags.diagnose(Loc, diag::super_invalid_context);
return nullptr;
} else if (!classDecl->hasSuperclass()) {
Ctx.Diags.diagnose(
Loc, diag::super_no_superclass,
/*isExtension*/ isa<ExtensionDecl>(typeContext->getAsDecl()),
classDecl);
return nullptr;
}
} else {
Ctx.Diags.diagnose(Loc, diag::super_invalid_context);
return nullptr;
}
// Do an actual lookup for 'self' in case it shows up in a capture list.
auto *methodSelf = methodContext->getImplicitSelfDecl();
auto *lookupSelf = ASTScope::lookupSingleLocalDecl(DC->getParentSourceFile(),
Ctx.Id_self, Loc);
if (lookupSelf && lookupSelf != methodSelf) {
// FIXME: This is the wrong diagnostic for if someone manually declares a
// variable named 'self' using backticks.
Ctx.Diags.diagnose(Loc, diag::super_in_closure_with_capture);
Ctx.Diags.diagnose(lookupSelf->getLoc(),
diag::super_in_closure_with_capture_here);
return nullptr;
}
return methodSelf;
}
/// Check whether this expression refers to the ~ operator.
static bool isTildeOperator(Expr *expr) {
auto nameMatches = [&](DeclName name) {
return name.isOperator() && name.getBaseName().getIdentifier().is("~");
};
if (auto overload = dyn_cast<OverloadedDeclRefExpr>(expr)) {
return llvm::any_of(overload->getDecls(), [=](auto *decl) -> bool {
return nameMatches(decl->getName());
});
}
if (auto unresolved = dyn_cast<UnresolvedDeclRefExpr>(expr)) {
return nameMatches(unresolved->getName().getFullName());
}
if (auto declRef = dyn_cast<DeclRefExpr>(expr)) {
return nameMatches(declRef->getDecl()->getName());
}
return false;
}
/// Simplify expressions which are type sugar productions that got parsed
/// as expressions due to the parser not knowing which identifiers are
/// type names.
TypeExpr *PreCheckTarget::simplifyTypeExpr(Expr *E) {
// If it's already a type expression, return it.
if (auto typeExpr = dyn_cast<TypeExpr>(E))
return typeExpr;
// Fold member types.
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(E)) {
return simplifyNestedTypeExpr(UDE);
}
// Fold '_' into a placeholder type, if we're allowed.
if (auto *DAE = dyn_cast<DiscardAssignmentExpr>(E)) {
if (canSimplifyDiscardAssignmentExpr(DAE)) {
auto *placeholderRepr =
new (Ctx) PlaceholderTypeRepr(DAE->getLoc());
return new (Ctx) TypeExpr(placeholderRepr);
}
}
// Fold T? into an optional type when T is a TypeExpr.
if (isa<OptionalEvaluationExpr>(E) || isa<BindOptionalExpr>(E)) {
TypeExpr *TyExpr;
SourceLoc QuestionLoc;
if (auto *OOE = dyn_cast<OptionalEvaluationExpr>(E)) {
TyExpr = dyn_cast<TypeExpr>(OOE->getSubExpr());
QuestionLoc = OOE->getLoc();
} else {
TyExpr = dyn_cast<TypeExpr>(cast<BindOptionalExpr>(E)->getSubExpr());
QuestionLoc = cast<BindOptionalExpr>(E)->getQuestionLoc();
}
if (!TyExpr) return nullptr;
auto *InnerTypeRepr = TyExpr->getTypeRepr();
assert(!TyExpr->isImplicit() && InnerTypeRepr &&
"This doesn't work on implicit TypeExpr's, "
"the TypeExpr should have been built correctly in the first place");
// The optional evaluation is passed through.
if (isa<OptionalEvaluationExpr>(E))
return TyExpr;
auto *NewTypeRepr =
new (Ctx) OptionalTypeRepr(InnerTypeRepr, QuestionLoc);
return new (Ctx) TypeExpr(NewTypeRepr);
}
// Fold T! into an IUO type when T is a TypeExpr.
if (auto *FVE = dyn_cast<ForceValueExpr>(E)) {
auto *TyExpr = dyn_cast<TypeExpr>(FVE->getSubExpr());
if (!TyExpr) return nullptr;
auto *InnerTypeRepr = TyExpr->getTypeRepr();
assert(!TyExpr->isImplicit() && InnerTypeRepr &&
"This doesn't work on implicit TypeExpr's, "
"the TypeExpr should have been built correctly in the first place");
auto *NewTypeRepr = new (Ctx)
ImplicitlyUnwrappedOptionalTypeRepr(InnerTypeRepr,
FVE->getExclaimLoc());
return new (Ctx) TypeExpr(NewTypeRepr);
}
// Fold (T) into a type T with parens around it.
if (auto *PE = dyn_cast<ParenExpr>(E)) {
auto *TyExpr = dyn_cast<TypeExpr>(PE->getSubExpr());
if (!TyExpr) return nullptr;
TupleTypeReprElement InnerTypeRepr[] = { TyExpr->getTypeRepr() };
assert(!TyExpr->isImplicit() && InnerTypeRepr[0].Type &&
"SubscriptExpr doesn't work on implicit TypeExpr's, "
"the TypeExpr should have been built correctly in the first place");
auto *NewTypeRepr = TupleTypeRepr::create(Ctx, InnerTypeRepr,
PE->getSourceRange());
return new (Ctx) TypeExpr(NewTypeRepr);
}
// Fold a tuple expr like (T1,T2) into a tuple type (T1,T2).
if (auto *TE = dyn_cast<TupleExpr>(E)) {
// FIXME: Decide what to do about (). It could be a type or an expr.
if (TE->getNumElements() == 0)
return nullptr;
SmallVector<TupleTypeReprElement, 4> Elts;
unsigned EltNo = 0;
for (auto Elt : TE->getElements()) {
// Try to simplify the element, e.g. to fold PackExpansionExprs
// into TypeExprs.
if (auto simplified = simplifyTypeExpr(Elt))
Elt = simplified;
auto *eltTE = dyn_cast<TypeExpr>(Elt);
if (!eltTE) return nullptr;
TupleTypeReprElement elt;
assert(eltTE->getTypeRepr() && !eltTE->isImplicit() &&
"This doesn't work on implicit TypeExpr's, the "
"TypeExpr should have been built correctly in the first place");
// If the tuple element has a label, propagate it.
elt.Type = eltTE->getTypeRepr();
elt.Name = TE->getElementName(EltNo);
elt.NameLoc = TE->getElementNameLoc(EltNo);
Elts.push_back(elt);
++EltNo;
}
auto *NewTypeRepr = TupleTypeRepr::create(
Ctx, Elts, TE->getSourceRange());
return new (Ctx) TypeExpr(NewTypeRepr);
}
// Fold [T] into an array type.
if (auto *AE = dyn_cast<ArrayExpr>(E)) {
if (AE->getElements().size() != 1)
return nullptr;
auto *TyExpr = dyn_cast<TypeExpr>(AE->getElement(0));
if (!TyExpr)
return nullptr;
auto *NewTypeRepr = new (Ctx)
ArrayTypeRepr(TyExpr->getTypeRepr(),
SourceRange(AE->getLBracketLoc(), AE->getRBracketLoc()));
return new (Ctx) TypeExpr(NewTypeRepr);
}
// Fold [K : V] into a dictionary type.
if (auto *DE = dyn_cast<DictionaryExpr>(E)) {
if (DE->getElements().size() != 1)
return nullptr;
TypeRepr *keyTypeRepr, *valueTypeRepr;
if (auto EltTuple = dyn_cast<TupleExpr>(DE->getElement(0))) {
auto *KeyTyExpr = dyn_cast<TypeExpr>(EltTuple->getElement(0));
if (!KeyTyExpr)
return nullptr;
auto *ValueTyExpr = dyn_cast<TypeExpr>(EltTuple->getElement(1));
if (!ValueTyExpr)
return nullptr;
keyTypeRepr = KeyTyExpr->getTypeRepr();
valueTypeRepr = ValueTyExpr->getTypeRepr();
} else {
auto *TE = dyn_cast<TypeExpr>(DE->getElement(0));
if (!TE) return nullptr;
auto *TRE = dyn_cast_or_null<TupleTypeRepr>(TE->getTypeRepr());
while (TRE->isParenType()) {
TRE = dyn_cast_or_null<TupleTypeRepr>(TRE->getElementType(0));
}
assert(TRE->getElements().size() == 2);
keyTypeRepr = TRE->getElementType(0);
valueTypeRepr = TRE->getElementType(1);
}
auto *NewTypeRepr = new (Ctx) DictionaryTypeRepr(
keyTypeRepr, valueTypeRepr,
/*FIXME:colonLoc=*/SourceLoc(),
SourceRange(DE->getLBracketLoc(), DE->getRBracketLoc()));
return new (Ctx) TypeExpr(NewTypeRepr);
}
// Reinterpret arrow expr T1 -> T2 as function type.
// FIXME: support 'inout', etc.
if (auto *AE = dyn_cast<ArrowExpr>(E)) {
if (!AE->isFolded()) return nullptr;
auto diagnoseMissingParens = [](ASTContext &ctx, TypeRepr *tyR) {
if (tyR->isSimpleUnqualifiedIdentifier(ctx.Id_Void)) {
ctx.Diags.diagnose(tyR->getStartLoc(), diag::function_type_no_parens)
.fixItReplace(tyR->getStartLoc(), "()");
} else {
ctx.Diags.diagnose(tyR->getStartLoc(), diag::function_type_no_parens)
.highlight(tyR->getSourceRange())
.fixItInsert(tyR->getStartLoc(), "(")
.fixItInsertAfter(tyR->getEndLoc(), ")");
}
};
auto extractInputTypeRepr = [&](Expr *E) -> TupleTypeRepr * {
if (!E)
return nullptr;
if (auto *TyE = dyn_cast<TypeExpr>(E)) {
auto ArgRepr = TyE->getTypeRepr();
if (auto *TTyRepr = dyn_cast<TupleTypeRepr>(ArgRepr))
return TTyRepr;
diagnoseMissingParens(Ctx, ArgRepr);
return TupleTypeRepr::create(Ctx, {ArgRepr}, ArgRepr->getSourceRange());
}
if (auto *TE = dyn_cast<TupleExpr>(E))
if (TE->getNumElements() == 0)
return TupleTypeRepr::createEmpty(Ctx, TE->getSourceRange());
// When simplifying a type expr like "(P1 & P2) -> (P3 & P4) -> Int",
// it may have been folded at the same time; recursively simplify it.
if (auto ArgsTypeExpr = simplifyTypeExpr(E)) {
auto ArgRepr = ArgsTypeExpr->getTypeRepr();
if (auto *TTyRepr = dyn_cast<TupleTypeRepr>(ArgRepr))
return TTyRepr;
diagnoseMissingParens(Ctx, ArgRepr);
return TupleTypeRepr::create(Ctx, {ArgRepr}, ArgRepr->getSourceRange());
}
return nullptr;
};
auto extractTypeRepr = [&](Expr *E) -> TypeRepr * {
if (!E)
return nullptr;
if (auto *TyE = dyn_cast<TypeExpr>(E))
return TyE->getTypeRepr();
if (auto *TE = dyn_cast<TupleExpr>(E))
if (TE->getNumElements() == 0)
return TupleTypeRepr::createEmpty(Ctx, TE->getSourceRange());
// When simplifying a type expr like "P1 & P2 -> P3 & P4 -> Int",
// it may have been folded at the same time; recursively simplify it.
if (auto ArgsTypeExpr = simplifyTypeExpr(E))
return ArgsTypeExpr->getTypeRepr();
return nullptr;
};
TupleTypeRepr *ArgsTypeRepr = extractInputTypeRepr(AE->getArgsExpr());
if (!ArgsTypeRepr) {
Ctx.Diags.diagnose(AE->getArgsExpr()->getLoc(),
diag::expected_type_before_arrow);
auto ArgRange = AE->getArgsExpr()->getSourceRange();
auto ErrRepr = ErrorTypeRepr::create(Ctx, ArgRange);
ArgsTypeRepr =
TupleTypeRepr::create(Ctx, {ErrRepr}, ArgRange);
}
TypeRepr *ThrownTypeRepr = nullptr;
if (auto thrownTypeExpr = AE->getThrownTypeExpr()) {
ThrownTypeRepr = extractTypeRepr(thrownTypeExpr);
assert(ThrownTypeRepr && "Parser ensures that this never fails");
}
TypeRepr *ResultTypeRepr = extractTypeRepr(AE->getResultExpr());
if (!ResultTypeRepr) {
Ctx.Diags.diagnose(AE->getResultExpr()->getLoc(),
diag::expected_type_after_arrow);
ResultTypeRepr =
ErrorTypeRepr::create(Ctx, AE->getResultExpr()->getSourceRange());
}
auto NewTypeRepr = new (Ctx)
FunctionTypeRepr(nullptr, ArgsTypeRepr, AE->getAsyncLoc(),
AE->getThrowsLoc(), ThrownTypeRepr, AE->getArrowLoc(),
ResultTypeRepr);
return new (Ctx) TypeExpr(NewTypeRepr);
}
// Fold '~P' into a composition type.
if (auto *unaryExpr = dyn_cast<PrefixUnaryExpr>(E)) {
if (isTildeOperator(unaryExpr->getFn())) {
if (auto operand = simplifyTypeExpr(unaryExpr->getOperand())) {
auto inverseTypeRepr = new (Ctx) InverseTypeRepr(
unaryExpr->getLoc(), operand->getTypeRepr());
return new (Ctx) TypeExpr(inverseTypeRepr);
}
}
}
// Fold 'P & Q' into a composition type
if (auto *binaryExpr = getCompositionExpr(E)) {
// The protocols we are composing
SmallVector<TypeRepr *, 4> Types;
auto lhsExpr = binaryExpr->getLHS();
if (auto *lhs = dyn_cast<TypeExpr>(lhsExpr)) {
Types.push_back(lhs->getTypeRepr());
} else if (isa<BinaryExpr>(lhsExpr)) {
// If the lhs is another binary expression, we have a multi element
// composition: 'A & B & C' is parsed as ((A & B) & C); we get
// the protocols from the lhs here
if (auto expr = simplifyTypeExpr(lhsExpr))
if (auto *repr = dyn_cast<CompositionTypeRepr>(expr->getTypeRepr()))
// add the protocols to our list
for (auto proto : repr->getTypes())
Types.push_back(proto);
else
return nullptr;
else
return nullptr;
} else
return nullptr;
// Add the rhs which is just a TypeExpr
auto *rhs = dyn_cast<TypeExpr>(binaryExpr->getRHS());
if (!rhs) return nullptr;
Types.push_back(rhs->getTypeRepr());
auto CompRepr = CompositionTypeRepr::create(Ctx, Types,
lhsExpr->getStartLoc(),
binaryExpr->getSourceRange());
return new (Ctx) TypeExpr(CompRepr);
}
// Fold a pack expansion expr into a TypeExpr when the pattern is a TypeExpr.
if (auto *expansion = dyn_cast<PackExpansionExpr>(E)) {
if (auto *pattern = dyn_cast<TypeExpr>(expansion->getPatternExpr())) {
auto *repr = new (Ctx) PackExpansionTypeRepr(expansion->getStartLoc(),
pattern->getTypeRepr());
return new (Ctx) TypeExpr(repr);
}
}
// Fold a PackElementExpr into a TypeExpr when the element is a TypeExpr
if (auto *element = dyn_cast<PackElementExpr>(E)) {
if (auto *refExpr = dyn_cast<TypeExpr>(element->getPackRefExpr())) {
auto *repr = new (Ctx) PackElementTypeRepr(element->getStartLoc(),
refExpr->getTypeRepr());
return new (Ctx) TypeExpr(repr);
}
}
return nullptr;
}
void PreCheckTarget::resolveKeyPathExpr(KeyPathExpr *KPE) {
if (KPE->isObjC())
return;
if (!KPE->getComponents().empty())
return;
TypeRepr *rootType = nullptr;
SmallVector<KeyPathExpr::Component, 4> components;
auto &DE = getASTContext().Diags;
// Pre-order visit of a sequence foo.bar[0]?.baz, which means that the
// components are pushed in reverse order.
auto traversePath = [&](Expr *expr, bool isInParsedPath,
bool emitErrors = true) {
Expr *outermostExpr = expr;
// We can end up in scenarios where the key path has contextual type,
// but is missing a leading dot. This can happen when we have an
// implicit TypeExpr or an implicit DeclRefExpr.
auto diagnoseMissingDot = [&]() {
DE.diagnose(expr->getLoc(),
diag::expr_swift_keypath_not_starting_with_dot)
.fixItInsert(expr->getStartLoc(), ".");
};
while (1) {
// Base cases: we've reached the top.
if (auto TE = dyn_cast<TypeExpr>(expr)) {
assert(!isInParsedPath);
rootType = TE->getTypeRepr();
if (TE->isImplicit() && !KPE->expectsContextualRoot())
diagnoseMissingDot();
return;
} else if (isa<KeyPathDotExpr>(expr)) {
assert(isInParsedPath);
// Nothing here: the type is either the root, or is inferred.
return;
} else if (!KPE->expectsContextualRoot() && expr->isImplicit() &&
isa<DeclRefExpr>(expr)) {
assert(!isInParsedPath);
diagnoseMissingDot();
return;
}
// Recurring cases:
if (auto SE = dyn_cast<DotSelfExpr>(expr)) {
// .self, the identity component.
components.push_back(KeyPathExpr::Component::forIdentity(
SE->getSelfLoc()));
expr = SE->getSubExpr();
} else if (auto UDE = dyn_cast<UnresolvedDotExpr>(expr)) {
// .foo, .foo() or .foo(val value: Int)
components.push_back(KeyPathExpr::Component::forUnresolvedMember(
UDE->getName(), UDE->getFunctionRefInfo(), UDE->getLoc()));
expr = UDE->getBase();
} else if (auto CCE = dyn_cast<CodeCompletionExpr>(expr)) {
components.push_back(
KeyPathExpr::Component::forCodeCompletion(CCE->getLoc()));
expr = CCE->getBase();
if (!expr) {
// We are completing on the key path's base. Stop iterating.
return;
}
} else if (auto SE = dyn_cast<SubscriptExpr>(expr)) {
// .[0] or just plain [0]
components.push_back(KeyPathExpr::Component::forUnresolvedSubscript(
getASTContext(), SE->getArgs()));
expr = SE->getBase();
} else if (auto BOE = dyn_cast<BindOptionalExpr>(expr)) {
// .? or ?
components.push_back(KeyPathExpr::Component::forUnresolvedOptionalChain(
BOE->getQuestionLoc()));
expr = BOE->getSubExpr();
} else if (auto FVE = dyn_cast<ForceValueExpr>(expr)) {
// .! or !
components.push_back(KeyPathExpr::Component::forUnresolvedOptionalForce(
FVE->getExclaimLoc()));
expr = FVE->getSubExpr();
} else if (auto OEE = dyn_cast<OptionalEvaluationExpr>(expr)) {
// Do nothing: this is implied to exist as the last expression, by the
// BindOptionalExprs, but is irrelevant to the components.
(void)outermostExpr;
assert(OEE == outermostExpr);
expr = OEE->getSubExpr();
} else if (auto CE = dyn_cast<CallExpr>(expr)) {
// foo(), foo(val value: Int) or unapplied foo
components.push_back(KeyPathExpr::Component::forUnresolvedApply(
getASTContext(), CE->getArgs()));
expr = CE->getFn();
} else {
if (emitErrors) {
// \(<expr>) may be an attempt to write a string interpolation outside
// of a string literal; diagnose this case specially.
if (isa<ParenExpr>(expr) || isa<TupleExpr>(expr)) {
DE.diagnose(expr->getLoc(),
diag::expr_string_interpolation_outside_string);
} else {
DE.diagnose(expr->getLoc(),
diag::expr_swift_keypath_invalid_component);
}
}
components.push_back(KeyPathExpr::Component());
return;
}
}
};
auto root = KPE->getParsedRoot();
auto path = KPE->getParsedPath();
if (path) {
traversePath(path, /*isInParsedPath=*/true);
// This path looks like \Foo.Bar.[0].baz, which means Foo.Bar has to be a
// type.
if (root) {
if (auto TE = dyn_cast<TypeExpr>(root)) {
rootType = TE->getTypeRepr();
} else {
// FIXME: Probably better to catch this case earlier and force-eval as
// TypeExpr.
DE.diagnose(root->getLoc(),
diag::expr_swift_keypath_not_starting_with_type);
// Traverse this path for recovery purposes: it may be a typo like
// \Foo.property.[0].
traversePath(root, /*isInParsedPath=*/false,
/*emitErrors=*/false);
}
}
} else {
traversePath(root, /*isInParsedPath=*/false);
}
// Key paths must be spelled with at least one component.
if (components.empty()) {
// Passes further down the pipeline expect keypaths to always have at least
// one component, so stuff an invalid component in the AST for recovery.
components.push_back(KeyPathExpr::Component());
}
std::reverse(components.begin(), components.end());
KPE->setExplicitRootType(rootType);
KPE->setComponents(getASTContext(), components);
}
Expr *PreCheckTarget::simplifyTypeConstructionWithLiteralArg(Expr *E) {
// If constructor call is expected to produce an optional let's not attempt
// this optimization because literal initializers aren't failable.
if (!getASTContext().LangOpts.isSwiftVersionAtLeast(5)) {
if (!ExprStack.empty()) {
auto *parent = ExprStack.back();
if (isa<BindOptionalExpr>(parent) || isa<ForceValueExpr>(parent))
return nullptr;
}
}
auto *call = dyn_cast<CallExpr>(E);
if (!call)
return nullptr;
auto *typeExpr = dyn_cast<TypeExpr>(call->getFn());
if (!typeExpr)
return nullptr;
auto *unaryArg = call->getArgs()->getUnlabeledUnaryExpr();
if (!unaryArg)
return nullptr;
auto *literal = dyn_cast<LiteralExpr>(unaryArg->getSemanticsProvidingExpr());
if (!literal)
return nullptr;
auto *protocol = TypeChecker::getLiteralProtocol(getASTContext(), literal);
if (!protocol)
return nullptr;
Type castTy;
if (auto precheckedTy = typeExpr->getInstanceType()) {
castTy = precheckedTy;
} else {
const auto result = TypeResolution::resolveContextualType(
typeExpr->getTypeRepr(), DC, TypeResolverContext::InExpression,
[](auto unboundTy) {
// FIXME: Don't let unbound generic types escape type resolution.
// For now, just return the unbound generic type.
return unboundTy;
},
// FIXME: Don't let placeholder types escape type resolution.
// For now, just return the placeholder type.
PlaceholderType::get,
// Pack elements for CoerceExprs are opened in CSGen.
/*packElementOpener*/ nullptr);
if (result->hasError())
return new (getASTContext())
ErrorExpr(typeExpr->getSourceRange(), result, typeExpr);
castTy = result;
}
if (!castTy->getAnyNominal())
return nullptr;
// Don't bother to convert deprecated selector syntax.
if (auto selectorTy = getASTContext().getSelectorType()) {
if (castTy->isEqual(selectorTy))
return nullptr;
}
return lookupConformance(castTy, protocol)
? CoerceExpr::forLiteralInit(getASTContext(), literal,
call->getSourceRange(),
typeExpr->getTypeRepr())
: nullptr;
}
/// Pull some operator expressions into the optional chain if needed.
///
/// foo? = newFoo // LHS of the assignment operator
/// foo?.bar += value // LHS of 'assignment: true' precedence group operators.
///
/// In such cases, the operand is constructed to be an 'OperatorEvaluationExpr'
/// wrapping the actual operand. This function hoist it and wraps the entire
/// expression with it. Returns the result 'OperatorEvaluationExpr', or nullptr
/// if 'expr' didn't match the condition.
OptionalEvaluationExpr *
PreCheckTarget::hoistOptionalEvaluationExprIfNeeded(Expr *expr) {
if (auto *assignE = dyn_cast<AssignExpr>(expr)) {
if (auto *OEE = dyn_cast<OptionalEvaluationExpr>(assignE->getDest())) {
assignE->setDest(OEE->getSubExpr());
OEE->setSubExpr(assignE);
return OEE;
}
} else if (auto *binaryE = dyn_cast<BinaryExpr>(expr)) {
if (auto *OEE = dyn_cast<OptionalEvaluationExpr>(binaryE->getLHS())) {
if (auto *precedence = TypeChecker::lookupPrecedenceGroupForInfixOperator(
DC, binaryE, /*diagnose=*/false)) {
if (precedence->isAssignment()) {
binaryE->getArgs()->setExpr(0, OEE->getSubExpr());
OEE->setSubExpr(binaryE);
return OEE;
}
}
}
}
return nullptr;
}
Expr *PreCheckTarget::wrapMemberChainIfNeeded(Expr *E) {
auto *parent = Parent.getAsExpr();
Expr *wrapped = E;
// If the parent is already wrapped, we've already formed the member chain.
if (parent && (isa<OptionalEvaluationExpr>(parent) ||
isa<UnresolvedMemberChainResultExpr>(parent))) {
return E;
}
// If we find an unresolved member chain, wrap it in an
// UnresolvedMemberChainResultExpr.
if (isMemberChainTail(E, parent, MemberChainKind::UnresolvedMember)) {
if (auto *UME = TypeChecker::getUnresolvedMemberChainBase(E))
wrapped = new (Ctx) UnresolvedMemberChainResultExpr(E, UME);
}
// Wrap optional chain in an OptionalEvaluationExpr.
if (isMemberChainTail(E, parent, MemberChainKind::OptionalBind)) {
if (isBindOptionalMemberChain(E))
wrapped = new (Ctx) OptionalEvaluationExpr(wrapped);
}
return wrapped;
}
bool ConstraintSystem::preCheckTarget(SyntacticElementTarget &target) {
auto *DC = target.getDeclContext();
auto &ctx = DC->getASTContext();
FrontendStatsTracer StatsTracer(ctx.Stats, "precheck-target");
auto newTarget = PreCheckTarget::check(target);
if (!newTarget)
return true;
target = *newTarget;
return false;
}