mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Check for `CallExpr` instead of `ApplyExpr`, we don't support arbitrary postfix operators in key paths.
2736 lines
99 KiB
C++
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;
|
|
}
|