mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
We were asserting (and doing the wrong thing) when trying to code
complete
nil #^HERE^#
The issue is that we tried to apply a solution to the expression that
contained free type variables (converted to generic parameters). This
trips us up when we expect the new type to conform to protocols. In code
completion we generally only need the type of the expression, so this
commit switches to getting that more explicitly. That said, this did
cause us to drop non-API parameter names in call patterns after an
opening '(' in some cases (covered by rdar://20962472).
Thanks to Doug for suggesting this solution!
rdar://problem/20891867
Swift SVN r28584
877 lines
30 KiB
C++
877 lines
30 KiB
C++
//===--- PlaygroundTransform.cpp - Playground Transform -------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the playground transform for Swift.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/ASTNode.h"
|
|
#include "swift/AST/ASTWalker.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/Expr.h"
|
|
#include "swift/AST/Module.h"
|
|
#include "swift/AST/NameLookup.h"
|
|
#include "swift/AST/Pattern.h"
|
|
#include "swift/AST/Stmt.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/Parse/Lexer.h"
|
|
#include "swift/Sema/CodeCompletionTypeChecking.h"
|
|
#include "swift/Subsystems.h"
|
|
#include "TypeChecker.h"
|
|
|
|
#include <forward_list>
|
|
#include <random>
|
|
|
|
using namespace swift;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// performPlaygroundTransform
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
class Instrumenter {
|
|
private:
|
|
std::mt19937_64 &RNG;
|
|
ASTContext &Context;
|
|
DeclContext *TypeCheckDC;
|
|
unsigned TmpNameIndex = 0;
|
|
bool HighPerformance;
|
|
|
|
struct BracePair {
|
|
public:
|
|
SourceRange BraceRange;
|
|
enum class TargetKinds {
|
|
None = 0,
|
|
Break,
|
|
Return,
|
|
Fallthrough
|
|
};
|
|
TargetKinds TargetKind = TargetKinds::None;
|
|
|
|
BracePair(const SourceRange &BR) :
|
|
BraceRange(BR) { }
|
|
};
|
|
|
|
typedef std::forward_list<BracePair> BracePairStack;
|
|
|
|
BracePairStack BracePairs;
|
|
class BracePairPusher {
|
|
BracePairStack &BracePairs;
|
|
bool Valid = false;
|
|
public:
|
|
BracePairPusher(BracePairStack &BPS, const SourceRange &BR) :
|
|
BracePairs(BPS) {
|
|
BracePairs.push_front(BracePair(BR));
|
|
Valid = true;
|
|
}
|
|
~BracePairPusher() {
|
|
if (isValid()) {
|
|
Valid = false;
|
|
BracePairs.pop_front();
|
|
}
|
|
}
|
|
void invalidate() {
|
|
if (isValid()) {
|
|
Valid = false;
|
|
BracePairs.pop_front();
|
|
}
|
|
}
|
|
bool isValid() {
|
|
return Valid;
|
|
}
|
|
};
|
|
|
|
class TargetKindSetter {
|
|
BracePairStack &BracePairs;
|
|
public:
|
|
TargetKindSetter(BracePairStack &BPS, BracePair::TargetKinds Kind) :
|
|
BracePairs(BPS) {
|
|
assert(!BracePairs.empty());
|
|
assert(BracePairs.front().TargetKind == BracePair::TargetKinds::None);
|
|
BracePairs.front().TargetKind = Kind;
|
|
}
|
|
~TargetKindSetter() {
|
|
BracePairs.front().TargetKind = BracePair::TargetKinds::None;
|
|
}
|
|
};
|
|
|
|
typedef llvm::SmallVector<swift::ASTNode, 3> ElementVector;
|
|
|
|
// Before a "return," "continue" or similar statement, emit pops of
|
|
// all the braces up to its target.
|
|
size_t escapeToTarget(BracePair::TargetKinds TargetKind,
|
|
ElementVector &Elements, size_t EI) {
|
|
if (HighPerformance)
|
|
return EI;
|
|
|
|
for (const BracePair &BP : BracePairs) {
|
|
if (BP.TargetKind == TargetKind) {
|
|
break;
|
|
}
|
|
Elements.insert(Elements.begin() + EI,
|
|
buildScopeExit(BP.BraceRange));
|
|
++EI;
|
|
}
|
|
return EI;
|
|
}
|
|
|
|
class ClosureFinder : public ASTWalker {
|
|
private:
|
|
Instrumenter &I;
|
|
public:
|
|
ClosureFinder (Instrumenter &Inst) : I(Inst) { }
|
|
virtual std::pair<bool, Stmt*> walkToStmtPre(Stmt *S) {
|
|
if (llvm::dyn_cast<BraceStmt>(S)) {
|
|
return { false, S }; // don't walk into brace statements; we
|
|
// need to respect nesting!
|
|
} else {
|
|
return { true, S };
|
|
}
|
|
}
|
|
virtual std::pair<bool, Expr*> walkToExprPre(Expr *E) {
|
|
if (ClosureExpr *CE = llvm::dyn_cast<ClosureExpr>(E)) {
|
|
BraceStmt *B = CE->getBody();
|
|
if (B) {
|
|
BraceStmt *NB = I.transformBraceStmt(B);
|
|
CE->setBody(NB, false);
|
|
// just with the entry and exit logging this is going to
|
|
// be more than a single expression!
|
|
}
|
|
}
|
|
return { true, E };
|
|
}
|
|
};
|
|
|
|
ClosureFinder CF;
|
|
|
|
public:
|
|
Instrumenter (ASTContext &C, DeclContext *DC, std::mt19937_64 &RNG,
|
|
bool HP) :
|
|
RNG(RNG), Context(C), TypeCheckDC(DC), HighPerformance(HP), CF(*this) { }
|
|
|
|
Stmt *transformStmt(Stmt *S) {
|
|
switch (S->getKind()) {
|
|
default:
|
|
return S;
|
|
case StmtKind::Brace:
|
|
return transformBraceStmt(llvm::cast<BraceStmt>(S));
|
|
case StmtKind::If:
|
|
return transformIfStmt(llvm::cast<IfStmt>(S));
|
|
case StmtKind::Guard:
|
|
return transformGuardStmt(llvm::cast<GuardStmt>(S));
|
|
case StmtKind::While: {
|
|
TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Break);
|
|
return transformWhileStmt(llvm::cast<WhileStmt>(S));
|
|
}
|
|
case StmtKind::RepeatWhile: {
|
|
TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Break);
|
|
return transformRepeatWhileStmt(llvm::cast<RepeatWhileStmt>(S));
|
|
}
|
|
case StmtKind::For: {
|
|
TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Break);
|
|
return transformForStmt(llvm::cast<ForStmt>(S));
|
|
}
|
|
case StmtKind::ForEach: {
|
|
TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Break);
|
|
return transformForEachStmt(llvm::cast<ForEachStmt>(S));
|
|
}
|
|
case StmtKind::Switch: {
|
|
TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Fallthrough);
|
|
return transformSwitchStmt(llvm::cast<SwitchStmt>(S));
|
|
}
|
|
}
|
|
}
|
|
|
|
// transform*() return their input if it's unmodified,
|
|
// or a modified copy of their input otherwise.
|
|
IfStmt *transformIfStmt(IfStmt *IS) {
|
|
if (Stmt *TS = IS->getThenStmt()) {
|
|
Stmt *NTS = transformStmt(TS);
|
|
if (NTS != TS) {
|
|
IS->setThenStmt(NTS);
|
|
}
|
|
}
|
|
|
|
if (Stmt *ES = IS->getElseStmt()) {
|
|
Stmt *NES = transformStmt(ES);
|
|
if (NES != ES) {
|
|
IS->setElseStmt(NES);
|
|
}
|
|
}
|
|
|
|
return IS;
|
|
}
|
|
|
|
GuardStmt *transformGuardStmt(GuardStmt *GS) {
|
|
if (Stmt *BS = GS->getBody())
|
|
GS->setBody(transformStmt(BS));
|
|
return GS;
|
|
}
|
|
|
|
WhileStmt *transformWhileStmt(WhileStmt *WS) {
|
|
if (Stmt *B = WS->getBody()) {
|
|
Stmt *NB = transformStmt(B);
|
|
if (NB != B) {
|
|
WS->setBody(NB);
|
|
}
|
|
}
|
|
|
|
return WS;
|
|
}
|
|
|
|
RepeatWhileStmt *transformRepeatWhileStmt(RepeatWhileStmt *RWS) {
|
|
if (Stmt *B = RWS->getBody()) {
|
|
Stmt *NB = transformStmt(B);
|
|
if (NB != B) {
|
|
RWS->setBody(NB);
|
|
}
|
|
}
|
|
|
|
return RWS;
|
|
}
|
|
|
|
ForStmt *transformForStmt(ForStmt *FS) {
|
|
if (Stmt *B = FS->getBody()) {
|
|
Stmt *NB = transformStmt(B);
|
|
if (NB != B) {
|
|
FS->setBody(NB);
|
|
}
|
|
}
|
|
|
|
return FS;
|
|
}
|
|
|
|
ForEachStmt *transformForEachStmt(ForEachStmt *FES) {
|
|
if (BraceStmt *B = FES->getBody()) {
|
|
BraceStmt *NB = transformBraceStmt(B);
|
|
if (NB != B) {
|
|
FES->setBody(NB);
|
|
}
|
|
}
|
|
|
|
return FES;
|
|
}
|
|
|
|
SwitchStmt *transformSwitchStmt(SwitchStmt *SS) {
|
|
for (CaseStmt *CS : SS->getCases()) {
|
|
if (Stmt *S = CS->getBody()) {
|
|
if (BraceStmt *B = llvm::dyn_cast<BraceStmt>(S)) {
|
|
BraceStmt *NB = transformBraceStmt(B);
|
|
if (NB != B) {
|
|
CS->setBody(NB);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return SS;
|
|
}
|
|
|
|
Decl *transformDecl(Decl *D) {
|
|
if (D->isImplicit())
|
|
return D;
|
|
if (FuncDecl *FD = llvm::dyn_cast<FuncDecl>(D)) {
|
|
if (BraceStmt *B = FD->getBody()) {
|
|
TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Return);
|
|
BraceStmt *NB = transformBraceStmt(B);
|
|
if (NB != B) {
|
|
FD->setBody(NB);
|
|
}
|
|
}
|
|
} else if (NominalTypeDecl *NTD = llvm::dyn_cast<NominalTypeDecl>(D)) {
|
|
for (Decl *Member : NTD->getMembers()) {
|
|
transformDecl(Member);
|
|
}
|
|
}
|
|
|
|
return D;
|
|
}
|
|
|
|
std::pair<Expr *, ValueDecl *> digForVariable(Expr *E) {
|
|
switch (E->getKind()) {
|
|
default:
|
|
if (ImplicitConversionExpr *ICE =
|
|
llvm::dyn_cast<ImplicitConversionExpr>(E)) {
|
|
return digForVariable(ICE->getSubExpr());
|
|
}
|
|
return std::make_pair(nullptr, nullptr);
|
|
case ExprKind::DeclRef:
|
|
return std::make_pair(E, llvm::cast<DeclRefExpr>(E)->getDecl());
|
|
case ExprKind::MemberRef:
|
|
return std::make_pair(
|
|
E, llvm::cast<MemberRefExpr>(E)->getMember().getDecl());
|
|
case ExprKind::Load:
|
|
return digForVariable(llvm::cast<LoadExpr>(E)->getSubExpr());
|
|
case ExprKind::ForceValue:
|
|
return digForVariable(llvm::cast<ForceValueExpr>(E)->getSubExpr());
|
|
case ExprKind::InOut:
|
|
return digForVariable( llvm::cast<InOutExpr>(E)->getSubExpr());
|
|
}
|
|
}
|
|
|
|
std::string digForName(Expr *E) {
|
|
Expr *RE = nullptr;
|
|
ValueDecl *VD = nullptr;
|
|
std::tie(RE, VD) = digForVariable(E);
|
|
if (VD) {
|
|
return VD->getName().str();
|
|
} else {
|
|
return std::string("");
|
|
}
|
|
}
|
|
|
|
static DeclRefExpr *digForInoutDeclRef(Expr *E) {
|
|
if (auto inout = dyn_cast<InOutExpr>(E)) {
|
|
return dyn_cast<DeclRefExpr>(
|
|
inout->getSubExpr()->getSemanticsProvidingExpr());
|
|
|
|
// Drill through tuple shuffles, ignoring non-default-argument inouts.
|
|
} else if (auto shuffle = dyn_cast<TupleShuffleExpr>(E)) {
|
|
return digForInoutDeclRef(shuffle->getSubExpr());
|
|
|
|
// Try to find a unique inout argument in a tuple.
|
|
} else if (auto tuple = dyn_cast<TupleExpr>(E)) {
|
|
DeclRefExpr *uniqueRef = nullptr;
|
|
for (Expr *elt : tuple->getElements()) {
|
|
if (auto ref = digForInoutDeclRef(elt)) {
|
|
// If we already have a reference, it's not unique.
|
|
if (uniqueRef) return nullptr;
|
|
uniqueRef = ref;
|
|
}
|
|
}
|
|
return uniqueRef;
|
|
|
|
// Look through parens.
|
|
} else {
|
|
auto subExpr = E->getSemanticsProvidingExpr();
|
|
return (E == subExpr ? nullptr : digForInoutDeclRef(subExpr));
|
|
}
|
|
}
|
|
|
|
BraceStmt *transformBraceStmt(BraceStmt *BS, bool TopLevel = false) {
|
|
llvm::ArrayRef<ASTNode> OriginalElements = BS->getElements();
|
|
typedef llvm::SmallVector<swift::ASTNode, 3> ElementVector;
|
|
ElementVector Elements(OriginalElements.begin(),
|
|
OriginalElements.end());
|
|
|
|
SourceRange SR = BS->getSourceRange();
|
|
BracePairPusher BPP(BracePairs, SR);
|
|
|
|
for (size_t EI = 0;
|
|
EI != Elements.size();
|
|
++EI) {
|
|
swift::ASTNode &Element = Elements[EI];
|
|
if (Expr *E = Element.dyn_cast<Expr*>()) {
|
|
E->walk(CF);
|
|
if (AssignExpr *AE = llvm::dyn_cast<AssignExpr>(E)) {
|
|
if (MemberRefExpr *MRE =
|
|
llvm::dyn_cast<MemberRefExpr>(AE->getDest())) {
|
|
// an assignment to a property of an object counts as a mutation of
|
|
// that object
|
|
Expr *Base_RE = nullptr;
|
|
ValueDecl *BaseVD = nullptr;
|
|
std::tie(Base_RE, BaseVD) = digForVariable(MRE->getBase());
|
|
|
|
if (Base_RE) {
|
|
Expr *Log = logDeclOrMemberRef(Base_RE);
|
|
if (Log) {
|
|
Elements.insert(Elements.begin() + (EI + 1), Log);
|
|
++EI;
|
|
}
|
|
}
|
|
} else {
|
|
std::pair<PatternBindingDecl *, VarDecl *> PV =
|
|
buildPatternAndVariable(AE->getSrc());
|
|
DeclRefExpr *DRE =
|
|
new (Context) DeclRefExpr(ConcreteDeclRef(PV.second),
|
|
SourceLoc(),
|
|
true, // implicit
|
|
AccessSemantics::Ordinary,
|
|
AE->getSrc()->getType());
|
|
AssignExpr *NAE = new (Context) AssignExpr(AE->getDest(),
|
|
SourceLoc(),
|
|
DRE,
|
|
true); // implicit
|
|
NAE->setType(Context.TheEmptyTupleType);
|
|
AE->setImplicit(true);
|
|
std::string Name = digForName(AE->getDest());
|
|
|
|
Expr *Log = buildLoggerCall(
|
|
new (Context) DeclRefExpr(ConcreteDeclRef(PV.second),
|
|
SourceLoc(),
|
|
true, // implicit
|
|
AccessSemantics::Ordinary,
|
|
AE->getSrc()->getType()),
|
|
AE->getSrc()->getSourceRange(), Name.c_str());
|
|
Elements[EI] = PV.first;
|
|
Elements.insert(Elements.begin() + (EI + 1), PV.second);
|
|
Elements.insert(Elements.begin() + (EI + 2), Log);
|
|
Elements.insert(Elements.begin() + (EI + 3), NAE);
|
|
EI += 3;
|
|
}
|
|
}
|
|
else if (ApplyExpr *AE = llvm::dyn_cast<ApplyExpr>(E)) {
|
|
bool Handled = false;
|
|
if (DeclRefExpr *DRE = llvm::dyn_cast<DeclRefExpr>(AE->getFn())) {
|
|
AbstractFunctionDecl *FnD =
|
|
llvm::dyn_cast<AbstractFunctionDecl>(DRE->getDecl());
|
|
if (FnD && FnD->getModuleContext() == Context.TheStdlibModule) {
|
|
StringRef FnName = FnD->getNameStr();
|
|
if (FnName.equals("print") || FnName.equals("debugPrint")) {
|
|
Expr *S = nullptr;
|
|
Expr *B = nullptr; // FIXME pass B to the logger when it supports it
|
|
if (ParenExpr *PE = llvm::dyn_cast<ParenExpr>(AE->getArg())) {
|
|
S = PE->getSubExpr();
|
|
} else if (TupleExpr *TE = llvm::dyn_cast<TupleExpr>(AE->getArg())) {
|
|
if (TE->getNumElements() == 2) {
|
|
S = TE->getElement(0);
|
|
B = TE->getElement(1);
|
|
}
|
|
}
|
|
if (S) {
|
|
std::pair<PatternBindingDecl *, VarDecl *> PV =
|
|
buildPatternAndVariable(S);
|
|
Expr *Log = logPrint(PV.second, AE->getSourceRange());
|
|
Elements[EI] = PV.first;
|
|
Elements.insert(Elements.begin() + (EI + 1), PV.second);
|
|
Elements.insert(Elements.begin() + (EI + 2), Log);
|
|
EI += 2;
|
|
}
|
|
Handled = true;
|
|
}
|
|
}
|
|
}
|
|
if (!Handled &&
|
|
AE->getType()->getCanonicalType() ==
|
|
Context.TheEmptyTupleType) {
|
|
if (DotSyntaxCallExpr *DSCE =
|
|
llvm::dyn_cast<DotSyntaxCallExpr>(AE->getFn())) {
|
|
Expr *TargetExpr = DSCE->getArg();
|
|
Expr *Target_RE = nullptr;
|
|
ValueDecl *TargetVD = nullptr;
|
|
|
|
std::tie(Target_RE, TargetVD) = digForVariable(TargetExpr);
|
|
|
|
if (TargetVD) {
|
|
Expr *Log = logDeclOrMemberRef(Target_RE);
|
|
if (Log) {
|
|
Elements.insert(Elements.begin() + (EI + 1), Log);
|
|
++EI;
|
|
}
|
|
}
|
|
} else if (DeclRefExpr *DRE = digForInoutDeclRef(AE->getArg())) {
|
|
Expr *Log = logDeclOrMemberRef(DRE);
|
|
if (Log) {
|
|
Elements.insert(Elements.begin() + (EI + 1), Log);
|
|
++EI;
|
|
}
|
|
}
|
|
Handled = true;
|
|
}
|
|
if (!Handled) {
|
|
// do the same as for all other expressions
|
|
std::pair<PatternBindingDecl *, VarDecl *> PV =
|
|
buildPatternAndVariable(E);
|
|
Expr *Log = buildLoggerCall(
|
|
new (Context) DeclRefExpr(ConcreteDeclRef(PV.second),
|
|
SourceLoc(),
|
|
true, // implicit
|
|
AccessSemantics::Ordinary,
|
|
E->getType()),
|
|
E->getSourceRange(), "");
|
|
Elements[EI] = PV.first;
|
|
Elements.insert(Elements.begin() + (EI + 1), PV.second);
|
|
Elements.insert(Elements.begin() + (EI + 2), Log);
|
|
EI += 2;
|
|
}
|
|
}
|
|
else {
|
|
if (E->getType()->getCanonicalType() !=
|
|
Context.TheEmptyTupleType) {
|
|
std::pair<PatternBindingDecl *, VarDecl *> PV =
|
|
buildPatternAndVariable(E);
|
|
Expr *Log = buildLoggerCall(
|
|
new (Context) DeclRefExpr(ConcreteDeclRef(PV.second),
|
|
SourceLoc(),
|
|
true, // implicit
|
|
AccessSemantics::Ordinary,
|
|
E->getType()),
|
|
E->getSourceRange(), "");
|
|
Elements[EI] = PV.first;
|
|
Elements.insert(Elements.begin() + (EI + 1), PV.second);
|
|
Elements.insert(Elements.begin() + (EI + 2), Log);
|
|
EI += 2;
|
|
}
|
|
}
|
|
} else if (Stmt *S = Element.dyn_cast<Stmt*>()) {
|
|
S->walk(CF);
|
|
if (ReturnStmt *RS = llvm::dyn_cast<ReturnStmt>(S)) {
|
|
if (RS->hasResult()) {
|
|
std::pair<PatternBindingDecl *, VarDecl *> PV =
|
|
buildPatternAndVariable(RS->getResult());
|
|
DeclRefExpr *DRE =
|
|
new (Context) DeclRefExpr(ConcreteDeclRef(PV.second),
|
|
SourceLoc(),
|
|
true, // implicit
|
|
AccessSemantics::Ordinary,
|
|
RS->getResult()->getType());
|
|
ReturnStmt *NRS = new (Context) ReturnStmt(SourceLoc(),
|
|
DRE,
|
|
true); // implicit
|
|
Expr *Log = buildLoggerCall(
|
|
new (Context) DeclRefExpr(ConcreteDeclRef(PV.second),
|
|
SourceLoc(),
|
|
true, // implicit
|
|
AccessSemantics::Ordinary,
|
|
RS->getResult()->getType()),
|
|
RS->getResult()->getSourceRange(), "");
|
|
Elements[EI] = PV.first;
|
|
Elements.insert(Elements.begin() + (EI + 1), PV.second);
|
|
Elements.insert(Elements.begin() + (EI + 2), Log);
|
|
Elements.insert(Elements.begin() + (EI + 3), NRS);
|
|
EI += 3;
|
|
}
|
|
EI = escapeToTarget(BracePair::TargetKinds::Return, Elements, EI);
|
|
} else {
|
|
if (llvm::isa<BreakStmt>(S) ||
|
|
llvm::isa<ContinueStmt>(S)) {
|
|
EI = escapeToTarget(BracePair::TargetKinds::Break, Elements, EI);
|
|
} else if (llvm::isa<FallthroughStmt>(S)) {
|
|
EI = escapeToTarget(BracePair::TargetKinds::Fallthrough, Elements,
|
|
EI);
|
|
}
|
|
Stmt *NS = transformStmt(S);
|
|
if (NS != S) {
|
|
Elements[EI] = NS;
|
|
}
|
|
}
|
|
} else if (Decl *D = Element.dyn_cast<Decl*>()) {
|
|
D->walk(CF);
|
|
if (auto *PBD = llvm::dyn_cast<PatternBindingDecl>(D)) {
|
|
if (VarDecl *VD = PBD->getSingleVar()) {
|
|
if (VD->getParentInitializer()) {
|
|
if (Expr *Log = logVarDecl(VD)) {
|
|
Elements.insert(Elements.begin() + (EI + 1), Log);
|
|
++EI;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
transformDecl(D);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!TopLevel && !HighPerformance) {
|
|
Elements.insert(Elements.begin(), buildScopeEntry(BS->getSourceRange()));
|
|
Elements.insert(Elements.end(), buildScopeExit(BS->getSourceRange()));
|
|
}
|
|
|
|
// Remove null elements from the list.
|
|
// FIXME: This is a band-aid used to work around the fact that the
|
|
// above code can introduce null elements into the vector. The
|
|
// right fix is to avoid doing that above.
|
|
Elements.erase(std::remove_if(Elements.begin(), Elements.end(),
|
|
[](ASTNode node) {
|
|
return node.isNull();
|
|
}),
|
|
Elements.end());
|
|
|
|
return swift::BraceStmt::create(Context, BS->getLBraceLoc(),
|
|
Context.AllocateCopy(Elements),
|
|
BS->getRBraceLoc());
|
|
}
|
|
|
|
// log*() functions return a newly-created log expression to be inserted
|
|
// after or instead of the expression they're looking at. Only call this
|
|
// if the variable has an initializer.
|
|
Expr *logVarDecl(VarDecl *VD) {
|
|
if (llvm::dyn_cast<ConstructorDecl>(TypeCheckDC) &&
|
|
VD->getNameStr().equals("self")) {
|
|
// Don't log "self" in a constructor
|
|
return nullptr;
|
|
}
|
|
|
|
return buildLoggerCall(
|
|
new (Context) DeclRefExpr(ConcreteDeclRef(VD),
|
|
SourceLoc(),
|
|
true, // implicit
|
|
AccessSemantics::Ordinary,
|
|
Type()),
|
|
VD->getSourceRange(), VD->getName().str().str().c_str());
|
|
}
|
|
|
|
Expr *logDeclOrMemberRef(Expr *RE) {
|
|
if (DeclRefExpr *DRE = llvm::dyn_cast<DeclRefExpr>(RE)) {
|
|
VarDecl *VD = llvm::cast<VarDecl>(DRE->getDecl());
|
|
|
|
if (llvm::dyn_cast<ConstructorDecl>(TypeCheckDC) &&
|
|
VD->getNameStr().equals("self")) {
|
|
// Don't log "self" in a constructor
|
|
return nullptr;
|
|
}
|
|
|
|
return buildLoggerCall(
|
|
new (Context) DeclRefExpr(ConcreteDeclRef(VD),
|
|
SourceLoc(),
|
|
true, // implicit
|
|
AccessSemantics::Ordinary,
|
|
Type()),
|
|
DRE->getSourceRange(), VD->getName().str().str().c_str());
|
|
} else if (MemberRefExpr *MRE = llvm::dyn_cast<MemberRefExpr>(RE)) {
|
|
Expr *B = MRE->getBase();
|
|
ConcreteDeclRef M = MRE->getMember();
|
|
|
|
if (llvm::dyn_cast<ConstructorDecl>(TypeCheckDC) &&
|
|
!digForName(B).compare("self")) {
|
|
// Don't log attributes of "self" in a constructor
|
|
return nullptr;
|
|
}
|
|
|
|
return buildLoggerCall(
|
|
new (Context) MemberRefExpr(B,
|
|
SourceLoc(),
|
|
M,
|
|
SourceRange(),
|
|
true, // implicit
|
|
AccessSemantics::Ordinary),
|
|
MRE->getSourceRange(), M.getDecl()->getName().str().str().c_str());
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
Expr *logPrint(VarDecl *VD, SourceRange SR) {
|
|
const char *LoggerName = "$builtin_print";
|
|
DeclRefExpr *DRE =
|
|
new (Context) DeclRefExpr(ConcreteDeclRef(VD),
|
|
SourceLoc(),
|
|
true, // implicit
|
|
AccessSemantics::Ordinary,
|
|
Type());
|
|
Expr *Args[] = { DRE };
|
|
return buildLoggerCallWithArgs(LoggerName, Args, SR);
|
|
}
|
|
|
|
std::pair<PatternBindingDecl*, VarDecl*>
|
|
buildPatternAndVariable(Expr *InitExpr) {
|
|
char NameBuf[11] = { 0 };
|
|
snprintf(NameBuf, 11, "tmp%u", TmpNameIndex);
|
|
TmpNameIndex++;
|
|
|
|
Expr *MaybeLoadInitExpr = nullptr;
|
|
|
|
if (LValueType *LVT =
|
|
llvm::dyn_cast<LValueType>(InitExpr->getType().getPointer())) {
|
|
MaybeLoadInitExpr = new (Context) LoadExpr (InitExpr,
|
|
LVT->getObjectType());
|
|
}
|
|
else {
|
|
MaybeLoadInitExpr = InitExpr;
|
|
}
|
|
|
|
VarDecl *VD = new (Context) VarDecl(false, // static
|
|
true, // let
|
|
SourceLoc(),
|
|
Context.getIdentifier(NameBuf),
|
|
MaybeLoadInitExpr->getType(),
|
|
TypeCheckDC);
|
|
|
|
VD->setImplicit();
|
|
|
|
NamedPattern *NP = new (Context) NamedPattern(VD, /*implicit*/true);
|
|
PatternBindingDecl *PBD =
|
|
PatternBindingDecl::create(Context, SourceLoc(), StaticSpellingKind::None,
|
|
SourceLoc(), NP, MaybeLoadInitExpr,
|
|
TypeCheckDC);
|
|
PBD->setImplicit();
|
|
|
|
return std::make_pair(PBD, VD);
|
|
}
|
|
|
|
Expr *buildLoggerCall(Expr *E, SourceRange SR, const char *Name) {
|
|
assert(Name);
|
|
std::string *NameInContext = Context.AllocateObjectCopy(std::string(Name));
|
|
|
|
Expr *NameExpr = new (Context) StringLiteralExpr(NameInContext->c_str(),
|
|
SourceRange());
|
|
NameExpr->setImplicit(true);
|
|
|
|
const size_t buf_size = 11;
|
|
char * const id_buf = (char*)Context.Allocate(buf_size, 1);
|
|
std::uniform_int_distribution<unsigned int> Distribution(0, 0x7fffffffu);
|
|
const unsigned int id_num = Distribution(RNG);
|
|
::snprintf(id_buf, buf_size, "%u", id_num);
|
|
Expr *IDExpr = new (Context) IntegerLiteralExpr(id_buf,
|
|
SR.End, true);
|
|
|
|
Expr *LoggerArgExprs[] = {
|
|
E,
|
|
NameExpr,
|
|
IDExpr
|
|
};
|
|
|
|
return buildLoggerCallWithArgs("$builtin_log_with_id",
|
|
MutableArrayRef<Expr *>(LoggerArgExprs),
|
|
SR);
|
|
}
|
|
|
|
Expr *buildScopeEntry(SourceRange SR) {
|
|
return buildScopeCall(SR, false);
|
|
}
|
|
|
|
Expr *buildScopeExit(SourceRange SR) {
|
|
return buildScopeCall(SR, true);
|
|
}
|
|
|
|
Expr *buildScopeCall(SourceRange SR, bool IsExit) {
|
|
const char *LoggerName = IsExit ? "$builtin_log_scope_exit"
|
|
: "$builtin_log_scope_entry";
|
|
|
|
return buildLoggerCallWithArgs(LoggerName,
|
|
MutableArrayRef<Expr *>(),
|
|
SR);
|
|
}
|
|
|
|
Expr *buildLoggerCallWithArgs(const char *LoggerName,
|
|
MutableArrayRef<Expr *> Args,
|
|
SourceRange SR) {
|
|
Expr *LoggerArgs = nullptr;
|
|
|
|
if (Args.size() == 1) {
|
|
LoggerArgs = new (Context) ParenExpr(SourceLoc(),
|
|
Args[0],
|
|
SourceLoc(),
|
|
false);
|
|
} else {
|
|
LoggerArgs = TupleExpr::createImplicit(Context, Args, { });
|
|
}
|
|
|
|
UnresolvedDeclRefExpr *LoggerRef =
|
|
new (Context) UnresolvedDeclRefExpr(
|
|
Context.getIdentifier(LoggerName),
|
|
DeclRefKind::Ordinary,
|
|
SR.End);
|
|
|
|
LoggerRef->setImplicit(true);
|
|
|
|
Expr *LoggerCall = new (Context) CallExpr(LoggerRef, LoggerArgs, true,
|
|
Type());
|
|
|
|
std::pair<unsigned, unsigned> StartLC =
|
|
Context.SourceMgr.getLineAndColumn(SR.Start);
|
|
|
|
std::pair<unsigned, unsigned> EndLC =
|
|
Context.SourceMgr.getLineAndColumn(
|
|
Lexer::getLocForEndOfToken(Context.SourceMgr, SR.End));
|
|
|
|
const size_t buf_size = 8;
|
|
|
|
char *start_line_buf = (char*)Context.Allocate(buf_size, 1);
|
|
char *end_line_buf = (char*)Context.Allocate(buf_size, 1);
|
|
char *start_column_buf = (char*)Context.Allocate(buf_size, 1);
|
|
char *end_column_buf = (char*)Context.Allocate(buf_size, 1);
|
|
|
|
::snprintf(start_line_buf, buf_size, "%d", StartLC.first);
|
|
::snprintf(start_column_buf, buf_size, "%d", StartLC.second);
|
|
::snprintf(end_line_buf, buf_size, "%d", EndLC.first);
|
|
::snprintf(end_column_buf, buf_size, "%d", EndLC.second);
|
|
|
|
Expr *StartLine = new (Context) IntegerLiteralExpr(start_line_buf,
|
|
SR.End, true);
|
|
Expr *EndLine = new (Context) IntegerLiteralExpr(end_line_buf,
|
|
SR.End, true);
|
|
Expr *StartColumn = new (Context) IntegerLiteralExpr(start_column_buf,
|
|
SR.End, true);
|
|
Expr *EndColumn = new (Context) IntegerLiteralExpr(end_column_buf,
|
|
SR.End, true);
|
|
|
|
Expr *SendDataArgExprs[] = {
|
|
LoggerCall,
|
|
StartLine,
|
|
EndLine,
|
|
StartColumn,
|
|
EndColumn
|
|
};
|
|
|
|
TupleExpr *SendDataArgs = TupleExpr::createImplicit(Context,
|
|
SendDataArgExprs, { });
|
|
UnresolvedDeclRefExpr *SendDataRef =
|
|
new (Context) UnresolvedDeclRefExpr(
|
|
Context.getIdentifier("$builtin_send_data"),
|
|
DeclRefKind::Ordinary,
|
|
SourceLoc());
|
|
|
|
SendDataRef->setImplicit(true);
|
|
|
|
Expr *SendDataCall = new (Context) CallExpr(SendDataRef, SendDataArgs, true,
|
|
Type());
|
|
|
|
if (!typeCheckContextExpr(Context, TypeCheckDC, SendDataCall)) {
|
|
return nullptr;
|
|
}
|
|
|
|
return SendDataCall;
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
void swift::performPlaygroundTransform(SourceFile &SF,
|
|
bool HighPerformance) {
|
|
class ExpressionFinder : public ASTWalker {
|
|
private:
|
|
std::mt19937_64 RNG;
|
|
bool HighPerformance;
|
|
public:
|
|
ExpressionFinder(bool HP) : HighPerformance(HP) { }
|
|
|
|
virtual bool walkToDeclPre(Decl *D) {
|
|
if (AbstractFunctionDecl *FD = llvm::dyn_cast<AbstractFunctionDecl>(D)) {
|
|
if (!FD->isImplicit()) {
|
|
if (BraceStmt *Body = FD->getBody()) {
|
|
ASTContext &ctx = FD->getASTContext();
|
|
Instrumenter I(ctx, FD, RNG, HighPerformance);
|
|
BraceStmt *NewBody = I.transformBraceStmt(Body);
|
|
if (NewBody != Body) {
|
|
FD->setBody(NewBody);
|
|
TypeChecker(ctx).checkFunctionErrorHandling(FD);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
} else if (TopLevelCodeDecl *TLCD = llvm::dyn_cast<TopLevelCodeDecl>(D)) {
|
|
if (!TLCD->isImplicit()) {
|
|
if (BraceStmt *Body = TLCD->getBody()) {
|
|
ASTContext &ctx = static_cast<Decl*>(TLCD)->getASTContext();
|
|
Instrumenter I(ctx, TLCD, RNG, HighPerformance);
|
|
BraceStmt *NewBody = I.transformBraceStmt(Body, true);
|
|
if (NewBody != Body) {
|
|
TLCD->setBody(NewBody);
|
|
TypeChecker(ctx).checkTopLevelErrorHandling(TLCD);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
ExpressionFinder EF(HighPerformance);
|
|
for (Decl* D : SF.Decls) {
|
|
D->walk(EF);
|
|
}
|
|
}
|