diff --git a/lib/Sema/PlaygroundTransform.cpp b/lib/Sema/PlaygroundTransform.cpp index fd721d9a8dd61..a64d68b7476a4 100644 --- a/lib/Sema/PlaygroundTransform.cpp +++ b/lib/Sema/PlaygroundTransform.cpp @@ -45,7 +45,7 @@ template class Added { E Contents; public: Added() { } - Added(E &&NewContents) { Contents = NewContents; } + Added(E NewContents) { Contents = NewContents; } const Added &operator=(const Added &rhs) { Contents = rhs.Contents; return *this; @@ -472,13 +472,17 @@ class Instrumenter { } }; - bool doTypeCheck(ASTContext &Ctx, DeclContext *DC, - Added &parsedExpr) { + template bool doTypeCheck(ASTContext &Ctx, + DeclContext *DC, + Added &parsedExpr) { DiagnosticEngine diags(Ctx.SourceMgr); ErrorGatherer errorGatherer(diags); TypeChecker TC(Ctx, diags); - TC.typeCheckExpression(*parsedExpr, DC); + + Expr *E = *parsedExpr; + TC.typeCheckExpression(E, DC); + parsedExpr = Added(dyn_cast(E)); if (*parsedExpr) { ErrorFinder errorFinder; @@ -515,7 +519,7 @@ class Instrumenter { std::tie(Base_RE, BaseVD) = digForVariable(MRE->getBase()); if (*Base_RE) { - Added Log = logDeclOrMemberRef(Base_RE); + Added Log = logDeclOrMemberRef(Base_RE); if (*Log) { Elements.insert(Elements.begin() + (EI + 1), *Log); ++EI; @@ -538,7 +542,7 @@ class Instrumenter { AE->setImplicit(true); std::string Name = digForName(AE->getDest()); - Added Log(buildLoggerCall( + Added Log(buildLoggerCall( new (Context) DeclRefExpr(ConcreteDeclRef(PV.second), SourceLoc(), true, // implicit @@ -563,41 +567,19 @@ class Instrumenter { StringRef FnName = FnD->getNameStr(); if (FnName.equals("print") || FnName.equals("debugPrint")) { const bool isDebugPrint = FnName.equals("debugPrint"); - Expr *object = nullptr; - Expr *appendNewline = nullptr; - if (ParenExpr *PE = dyn_cast(AE->getArg())) { - object = PE->getSubExpr(); - Added appendNewlineAdded( - new (Context) BooleanLiteralExpr(true, SourceLoc(), true)); - doTypeCheck(Context, TypeCheckDC, appendNewlineAdded); - appendNewline = *appendNewlineAdded; - } else if (TupleExpr *TE = dyn_cast(AE->getArg())) { - if (TE->getNumElements() == 2) { - object = TE->getElement(0); - appendNewline = TE->getElement(1); - } - } - if (object && !object->getType()->is() && - appendNewline && !appendNewline->getType()->is()) { - std::pair objectPV = - buildPatternAndVariable(object); - std::pair appendNewlinePV = - buildPatternAndVariable(appendNewline); - Added Log = logPrint(isDebugPrint, - objectPV.second, - appendNewlinePV.second, - AE->getSourceRange()); - if (*Log) { - Elements[EI] = objectPV.first; - Elements.insert(Elements.begin() + (EI + 1), - objectPV.second); - Elements.insert(Elements.begin() + (EI + 2), - appendNewlinePV.first); - Elements.insert(Elements.begin() + (EI + 3), - appendNewlinePV.second); - Elements.insert(Elements.begin() + (EI + 4), - *Log); - EI += 4; + PatternBindingDecl *ArgPattern = nullptr; + VarDecl *ArgVariable = nullptr; + Added Log = logPrint(isDebugPrint, AE, + ArgPattern, ArgVariable); + if (*Log) { + if (ArgPattern) { + assert(ArgVariable); + Elements[EI] = ArgPattern; + Elements.insert(Elements.begin() + (EI + 1), ArgVariable); + Elements.insert(Elements.begin() + (EI + 2), *Log); + EI += 2; + } else { + Elements[EI] = *Log; } } Handled = true; @@ -615,14 +597,14 @@ class Instrumenter { std::tie(Target_RE, TargetVD) = digForVariable(TargetExpr); if (TargetVD) { - Added Log = logDeclOrMemberRef(Target_RE); + Added Log = logDeclOrMemberRef(Target_RE); if (*Log) { Elements.insert(Elements.begin() + (EI + 1), *Log); ++EI; } } } else if (DeclRefExpr *DRE = digForInoutDeclRef(AE->getArg())) { - Added Log = logDeclOrMemberRef(DRE); + Added Log = logDeclOrMemberRef(DRE); if (*Log) { Elements.insert(Elements.begin() + (EI + 1), *Log); ++EI; @@ -634,7 +616,7 @@ class Instrumenter { // do the same as for all other expressions std::pair PV = buildPatternAndVariable(E); - Added Log = buildLoggerCall( + Added Log = buildLoggerCall( new (Context) DeclRefExpr(ConcreteDeclRef(PV.second), SourceLoc(), true, // implicit @@ -654,7 +636,7 @@ class Instrumenter { Context.TheEmptyTupleType) { std::pair PV = buildPatternAndVariable(E); - Added Log = buildLoggerCall( + Added Log = buildLoggerCall( new (Context) DeclRefExpr(ConcreteDeclRef(PV.second), SourceLoc(), true, // implicit @@ -684,7 +666,7 @@ class Instrumenter { ReturnStmt *NRS = new (Context) ReturnStmt(SourceLoc(), DRE, true); // implicit - Added Log = buildLoggerCall( + Added Log = buildLoggerCall( new (Context) DeclRefExpr(ConcreteDeclRef(PV.second), SourceLoc(), true, // implicit @@ -717,7 +699,7 @@ class Instrumenter { if (auto *PBD = dyn_cast(D)) { if (VarDecl *VD = PBD->getSingleVar()) { if (VD->getParentInitializer()) { - Added Log = logVarDecl(VD); + Added Log = logVarDecl(VD); if (*Log) { Elements.insert(Elements.begin() + (EI + 1), *Log); ++EI; @@ -753,7 +735,7 @@ class Instrumenter { // 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. - Added logVarDecl(VarDecl *VD) { + Added logVarDecl(VarDecl *VD) { if (isa(TypeCheckDC) && VD->getNameStr().equals("self")) { // Don't log "self" in a constructor return nullptr; @@ -768,7 +750,7 @@ class Instrumenter { VD->getSourceRange(), VD->getName().str().str().c_str()); } - Added logDeclOrMemberRef(Added RE) { + Added logDeclOrMemberRef(Added RE) { if (DeclRefExpr *DRE = dyn_cast(*RE)) { VarDecl *VD = cast(DRE->getDecl()); @@ -807,24 +789,87 @@ class Instrumenter { } } - Added logPrint(bool isDebugPrint, VarDecl *object, - VarDecl *appendNewline, SourceRange SR) { + std::pair + maybeFixupPrintArgument(ApplyExpr *Print) { + Expr *ArgTuple = Print->getArg(); + if (ParenExpr *PE = dyn_cast(ArgTuple)) { + std::pair PV = + buildPatternAndVariable(PE->getSubExpr()); + PE->setSubExpr(new (Context) DeclRefExpr(ConcreteDeclRef(PV.second), + SourceLoc(), + true, // implicit + AccessSemantics::Ordinary, + PE->getSubExpr()->getType())); + return PV; + } else if (TupleExpr *TE = dyn_cast(ArgTuple)) { + if (TE->getNumElements() == 0) { + return std::make_pair(nullptr, nullptr); + } else { + // Are we using print() specialized to handle a single argument, + // or is actually only the first argument of interest and the rest are + // extra information for print()? + bool useJustFirst = false; + if (TE->hasElementNames()) { + useJustFirst = true; + } else { + for (Expr *Arg : TE->getElements()) { + if (Arg->getType()->getAs()) { + useJustFirst = true; + break; + } + } + } + if (useJustFirst) { + std::pair PV = + buildPatternAndVariable(TE->getElement(0)); + TE->setElement(0, + new (Context) DeclRefExpr( + ConcreteDeclRef(PV.second), + SourceLoc(), + true, // implicit + AccessSemantics::Ordinary, + TE->getElement(0)->getType())); + return PV; + } else { + std::pair PV = + buildPatternAndVariable(TE); + Print->setArg(new (Context) DeclRefExpr( + ConcreteDeclRef(PV.second), + SourceLoc(), + true, // implicit + AccessSemantics::Ordinary, + TE->getType())); + return PV; + } + } + } else { + return std::make_pair(nullptr, nullptr); + } + } + + Added logPrint(bool isDebugPrint, ApplyExpr *AE, + PatternBindingDecl *&ArgPattern, + VarDecl *&ArgVariable) { const char *LoggerName = isDebugPrint ? "$builtin_debugPrint" : "$builtin_print"; - DeclRefExpr *object_DRE = - new (Context) DeclRefExpr(ConcreteDeclRef(object), - SourceLoc(), - true, // implicit - AccessSemantics::Ordinary, - Type()); - DeclRefExpr *appendNewline_DRE = - new (Context) DeclRefExpr(ConcreteDeclRef(appendNewline), - SourceLoc(), - true, // implicit - AccessSemantics::Ordinary, - Type()); - Expr *Args[] = { object_DRE, appendNewline_DRE }; - return buildLoggerCallWithArgs(LoggerName, Args, SR); + + UnresolvedDeclRefExpr *LoggerRef = + new (Context) UnresolvedDeclRefExpr( + Context.getIdentifier(LoggerName), + DeclRefKind::Ordinary, + AE->getSourceRange().End); + + std::tie(ArgPattern, ArgVariable) = + maybeFixupPrintArgument(AE); + + AE->setFn(LoggerRef); + Added AddedApply(AE); // safe because we've fixed up the args + + if (!doTypeCheck(Context, TypeCheckDC, AddedApply)) { + return nullptr; + } + + return buildLoggerCallWithApply(AddedApply, AE->getSourceRange()); } std::pair @@ -863,7 +908,7 @@ class Instrumenter { return std::make_pair(PBD, VD); } - Added buildLoggerCall(Added E, SourceRange SR, + Added buildLoggerCall(Added E, SourceRange SR, const char *Name) { assert(Name); std::string *NameInContext = Context.AllocateObjectCopy(std::string(Name)); @@ -891,15 +936,15 @@ class Instrumenter { SR); } - Added buildScopeEntry(SourceRange SR) { + Added buildScopeEntry(SourceRange SR) { return buildScopeCall(SR, false); } - Added buildScopeExit(SourceRange SR) { + Added buildScopeExit(SourceRange SR) { return buildScopeCall(SR, true); } - Added buildScopeCall(SourceRange SR, bool IsExit) { + Added buildScopeCall(SourceRange SR, bool IsExit) { const char *LoggerName = IsExit ? "$builtin_log_scope_exit" : "$builtin_log_scope_entry"; @@ -908,7 +953,7 @@ class Instrumenter { SR); } - Added buildLoggerCallWithArgs(const char *LoggerName, + Added buildLoggerCallWithArgs(const char *LoggerName, MutableArrayRef Args, SourceRange SR) { Expr *LoggerArgs = nullptr; @@ -930,9 +975,20 @@ class Instrumenter { LoggerRef->setImplicit(true); - Expr *LoggerCall = new (Context) CallExpr(LoggerRef, LoggerArgs, true, - Type()); + ApplyExpr *LoggerCall = new (Context) CallExpr(LoggerRef, LoggerArgs, true, + Type()); + Added AddedLogger(LoggerCall); + + if (!doTypeCheck(Context, TypeCheckDC, AddedLogger)) { + return nullptr; + } + + return buildLoggerCallWithApply(AddedLogger, SR); + } + // Assumes Apply has already been type-checked. + Added buildLoggerCallWithApply(Added Apply, + SourceRange SR) { std::pair StartLC = Context.SourceMgr.getLineAndColumn(SR.Start); @@ -961,8 +1017,17 @@ class Instrumenter { Expr *EndColumn = new (Context) IntegerLiteralExpr(end_column_buf, SR.End, true); + std::pair PV = + buildPatternAndVariable(*Apply); + + DeclRefExpr *DRE = new (Context) DeclRefExpr(ConcreteDeclRef(PV.second), + SourceLoc(), + true, // implicit + AccessSemantics::Ordinary, + Apply->getType()); + Expr *SendDataArgExprs[] = { - LoggerCall, + DRE, StartLine, EndLine, StartColumn, @@ -979,15 +1044,25 @@ class Instrumenter { SendDataRef->setImplicit(true); - Added SendDataCall = new (Context) CallExpr(SendDataRef, - SendDataArgs, true, - Type()); + Expr * SendDataCall = new (Context) CallExpr(SendDataRef, + SendDataArgs, true, + Type()); + Added AddedSendData(SendDataCall); - if (!doTypeCheck(Context, TypeCheckDC, SendDataCall)) { + if (!doTypeCheck(Context, TypeCheckDC, AddedSendData)) { return nullptr; } - return SendDataCall; + ASTNode Elements[] = { + PV.first, + PV.second, + SendDataCall + }; + + BraceStmt *BS = BraceStmt::create(Context, SourceLoc(), Elements, + SourceLoc(), true); + + return BS; } }; diff --git a/test/PlaygroundTransform/Inputs/PlaygroundsRuntime.swift b/test/PlaygroundTransform/Inputs/PlaygroundsRuntime.swift index 965e629ce0a78..d0c0c4bbb3ece 100644 --- a/test/PlaygroundTransform/Inputs/PlaygroundsRuntime.swift +++ b/test/PlaygroundTransform/Inputs/PlaygroundsRuntime.swift @@ -1,3 +1,7 @@ +// If you're modifying this file to add or modify function signatures, you +// should be notifying the maintainer of PlaygroundLogger and also the +// maintainer of lib/Sema/PlaygroundTransform.cpp. + class LogRecord { let text : String init(api : String, object : Any, name : String, id : Int) { @@ -36,7 +40,15 @@ func $builtin_log_scope_exit() -> AnyObject? { return LogRecord(api:"$builtin_log_scope_exit") } -func $builtin_print(object: T, _ appendNewline: Bool) -> AnyObject? { +func $builtin_print(object: T) -> AnyObject? { + return LogRecord(api: "$builtin_print<>", object: object) +} + +func $builtin_print(object: T, inout _ stream: TargetStream) -> AnyObject? { + return LogRecord(api: "$builtin_print", object: object) +} + +func $builtin_print(object: T, appendNewline: Bool) -> AnyObject? { if (appendNewline) { return LogRecord(api: "$builtin_print", object: object) } else { @@ -44,7 +56,23 @@ func $builtin_print(object: T, _ appendNewline: Bool) -> AnyObject? { } } -func $builtin_debugPrint(object: T, _ appendNewline: Bool) -> AnyObject? { +func $builtin_print(object: T, inout _ stream: TargetStream, appendNewline: Bool) -> AnyObject? { + if (appendNewline) { + return LogRecord(api: "$builtin_print", object: object) + } else { + return LogRecord(api: "$builtin_print", object: object) + } +} + +func $builtin_debugPrint(object: T) -> AnyObject? { + return LogRecord(api: "$builtin_debugPrint<>", object: object) +} + +func $builtin_debugPrint(object: T, inout _ stream: TargetStream) -> AnyObject? { + return LogRecord(api: "$builtin_debugPrint", object: object) +} + +func $builtin_debugPrint(object: T, appendNewline: Bool) -> AnyObject? { if (appendNewline) { return LogRecord(api: "$builtin_debugPrint", object: object) } else { @@ -52,6 +80,14 @@ func $builtin_debugPrint(object: T, _ appendNewline: Bool) -> AnyObject? { } } +func $builtin_debugPrint(object: T, inout _ stream: TargetStream, appendNewline: Bool) -> AnyObject? { + if (appendNewline) { + return LogRecord(api: "$builtin_debugPrint", object: object) + } else { + return LogRecord(api: "$builtin_debugPrint", object: object) + } +} + func $builtin_send_data(object:AnyObject?, _ sl: Int, _ el: Int, _ sc: Int, _ ec: Int) { let loc = "[\(sl):\(sc)-\(el):\(ec)]" print(loc + " " + (object as! LogRecord).text) diff --git a/test/PlaygroundTransform/array_did_set.swift b/test/PlaygroundTransform/array_did_set.swift index 96369d4a662e0..7058135c1eb13 100644 --- a/test/PlaygroundTransform/array_did_set.swift +++ b/test/PlaygroundTransform/array_did_set.swift @@ -17,10 +17,10 @@ s.a.append(300) // CHECK: [{{.*}}] $builtin_log[s='S(a: [])'] // CHECK-NEXT: [{{.*}}] $builtin_log_scope_entry -// CHECK-NEXT: [{{.*}}] $builtin_print['Set'] +// CHECK-NEXT: [{{.*}}] $builtin_print<>['Set'] // CHECK-NEXT: [{{.*}}] $builtin_log_scope_exit // CHECK-NEXT: [{{.*}}] $builtin_log[s='S(a: [3, 2])'] // CHECK-NEXT: [{{.*}}] $builtin_log_scope_entry -// CHECK-NEXT: [{{.*}}] $builtin_print['Set'] +// CHECK-NEXT: [{{.*}}] $builtin_print<>['Set'] // CHECK-NEXT: [{{.*}}] $builtin_log_scope_exit // CHECK-NEXT: [{{.*}}] $builtin_log[a='[3, 2, 300]'] diff --git a/test/PlaygroundTransform/print.swift b/test/PlaygroundTransform/print.swift index be34d468adb4a..9e62728e19d27 100644 --- a/test/PlaygroundTransform/print.swift +++ b/test/PlaygroundTransform/print.swift @@ -5,29 +5,36 @@ // RUN: %target-run %t/main | FileCheck %s // REQUIRES: executable_test -var a = 2 -print(a) // with newline -print(a, appendNewline: true) // with newline -print(a, appendNewline: false) // no newline +var str : String = "" -var b = 3 -debugPrint(b+1) // with newline -debugPrint(b+1, appendNewline: true) // with newline -debugPrint(b+1, appendNewline: false) // no newline +print("One") +print("One", 2) +print("One", &str) +print("One", appendNewline:true) +print("One", appendNewline:false) +print("One", &str, appendNewline:true) +print("One", &str, appendNewline:false) -debugPrint(b+1, appendNewline: { false }()) +debugPrint("One") +debugPrint("One", 2) +debugPrint("One", &str) +debugPrint("One", appendNewline:true) +debugPrint("One", appendNewline:false) +debugPrint("One", &str, appendNewline:true) +debugPrint("One", &str, appendNewline:false) -// CHECK: [{{.*}}] $builtin_log[a='2'] -// CHECK-NEXT: [{{.*}}] $builtin_print['2'] -// CHECK-NEXT: [{{.*}}] $builtin_print['2'] -// CHECK-NEXT: [{{.*}}] $builtin_print['2'] -// CHECK-NEXT: [{{.*}}] $builtin_log[b='3'] -// CHECK-NEXT: [{{.*}}] $builtin_debugPrint['4'] -// CHECK-NEXT: [{{.*}}] $builtin_debugPrint['4'] -// CHECK-NEXT: [{{.*}}] $builtin_debugPrint['4'] -// CHECK-NEXT: [{{.*}}] $builtin_log_scope_entry -// CHECK-NEXT: [{{.*}}] $builtin_log[='false'] -// CHECK-NEXT: [{{.*}}] $builtin_log_scope_exit -// CHECK-NEXT: [{{.*}}] $builtin_log_scope_exit -// CHECK-NEXT: [{{.*}}] $builtin_debugPrint['4'] +// CHECK: [{{.*}}] $builtin_print<>['One'] +// CHECK-NEXT: [{{.*}}] $builtin_print<>['("One", 2)'] +// CHECK-NEXT: [{{.*}}] $builtin_print['One'] +// CHECK-NEXT: [{{.*}}] $builtin_print['One'] +// CHECK-NEXT: [{{.*}}] $builtin_print['One'] +// CHECK-NEXT: [{{.*}}] $builtin_print['One'] +// CHECK-NEXT: [{{.*}}] $builtin_print['One'] +// CHECK-NEXT: [{{.*}}] $builtin_debugPrint<>['One'] +// CHECK-NEXT: [{{.*}}] $builtin_debugPrint<>['("One", 2)'] +// CHECK-NEXT: [{{.*}}] $builtin_debugPrint['One'] +// CHECK-NEXT: [{{.*}}] $builtin_debugPrint['One'] +// CHECK-NEXT: [{{.*}}] $builtin_debugPrint['One'] +// CHECK-NEXT: [{{.*}}] $builtin_debugPrint['One'] +// CHECK-NEXT: [{{.*}}] $builtin_debugPrint['One'] diff --git a/test/PlaygroundTransform/print_inout.swift b/test/PlaygroundTransform/print_inout.swift deleted file mode 100644 index b6366fa240b4a..0000000000000 --- a/test/PlaygroundTransform/print_inout.swift +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: rm -rf %t && mkdir %t -// RUN: cp %s %t/main.swift -// RUN: %target-build-swift -Xfrontend -playground -Xfrontend -debugger-support -o %t/main %S/Inputs/PlaygroundsRuntime.swift %t/main.swift -// RUN: %target-run %t/main | FileCheck %s -// REQUIRES: executable_test - -// FIXME: deal with inout arguments to print -// XFAIL: * - -var s = "a" -print("b", &s) -// CHECK: [{{.*}}] $builtin_log[s='a'] -// CHECK-NEXT: [{{.*}}] $builtin_print['a']