Skip to content

Commit 2b5a2ad

Browse files
committed
Rework the integrated REPL to use separate modules for every line
...like LLDB does, instead of parsing into a single SourceFile. This does break some functionality: - no more :dump_ast - no redeclaration checking, but no shadowing either---redeclarations just become ambiguous - pretty much requires EnableAccessControl to be off, since we don't walk decls to promote them to 'public' ...but it allows us to remove a bit of longstanding support for type-checking / SILGen-ing / IRGen-ing only part of a SourceFile that was only used by the integrated REPL. ...which, need I remind everyone, is still /deprecated/...but sometimes convenient. So most of it still works.
1 parent f1856b8 commit 2b5a2ad

File tree

5 files changed

+97
-94
lines changed

5 files changed

+97
-94
lines changed

lib/AST/Module.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,12 @@ SourceFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const
13051305
if (next->getName() == getParentModule()->getName())
13061306
return true;
13071307

1308+
// Hack: Assume other REPL files already have their libraries linked.
1309+
if (!next->getFiles().empty())
1310+
if (auto *nextSource = dyn_cast<SourceFile>(next->getFiles().front()))
1311+
if (nextSource->Kind == SourceFileKind::REPL)
1312+
return true;
1313+
13081314
next->collectLinkLibraries(callback);
13091315
return true;
13101316
});

lib/Immediate/REPL.cpp

Lines changed: 83 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,6 @@ using namespace swift::immediate;
4646

4747
namespace {
4848

49-
class REPLContext {
50-
public:
51-
/// The SourceMgr buffer ID of the REPL input.
52-
unsigned CurBufferID;
53-
54-
/// The index into the source file's Decls at which to start
55-
/// typechecking the next REPL input.
56-
unsigned CurElem;
57-
58-
/// The index into the source file's Decls at which to start
59-
/// irgenning the next REPL input.
60-
unsigned CurIRGenElem;
61-
};
62-
6349
enum class REPLInputKind : int {
6450
/// The REPL got a "quit" signal.
6551
REPLQuit,
@@ -168,27 +154,49 @@ static void convertToUTF8(llvm::ArrayRef<wchar_t> wide,
168154

169155
#if HAVE_UNICODE_LIBEDIT
170156

171-
static bool appendToREPLFile(SourceFile &SF,
172-
PersistentParserState &PersistentState,
173-
REPLContext &RC,
174-
std::unique_ptr<llvm::MemoryBuffer> Buffer) {
175-
assert(SF.Kind == SourceFileKind::REPL && "Can't append to a non-REPL file");
176-
177-
SourceManager &SrcMgr = SF.getParentModule()->getASTContext().SourceMgr;
178-
RC.CurBufferID = SrcMgr.addNewSourceBuffer(std::move(Buffer));
157+
static ModuleDecl *
158+
typeCheckREPLInput(ModuleDecl *MostRecentModule, StringRef Name,
159+
PersistentParserState &PersistentState,
160+
std::unique_ptr<llvm::MemoryBuffer> Buffer) {
161+
using ImplicitModuleImportKind = SourceFile::ImplicitModuleImportKind;
162+
assert(MostRecentModule);
163+
ASTContext &Ctx = MostRecentModule->getASTContext();
164+
165+
auto REPLModule = ModuleDecl::create(Ctx.getIdentifier(Name), Ctx);
166+
auto BufferID = Ctx.SourceMgr.addNewSourceBuffer(std::move(Buffer));
167+
auto ImportKind = ImplicitModuleImportKind::None;
168+
auto &REPLInputFile = *new (Ctx) SourceFile(*REPLModule, SourceFileKind::REPL,
169+
BufferID, ImportKind);
170+
REPLModule->addFile(REPLInputFile);
171+
172+
ModuleDecl::ImportedModule ImportOfMostRecentModule{
173+
/*AccessPath*/{}, MostRecentModule};
174+
REPLInputFile.addImports(std::make_pair(ImportOfMostRecentModule,
175+
SourceFile::ImportOptions()));
176+
177+
SmallVector<ModuleDecl::ImportedModule, 8> Imports;
178+
MostRecentModule->getImportedModules(Imports,
179+
ModuleDecl::ImportFilter::Private);
180+
if (!Imports.empty()) {
181+
SmallVector<std::pair<ModuleDecl::ImportedModule,
182+
SourceFile::ImportOptions>, 8> ImportsWithOptions;
183+
for (auto Import : Imports) {
184+
ImportsWithOptions.emplace_back(Import,
185+
SourceFile::ImportFlags::Exported);
186+
}
187+
REPLInputFile.addImports(ImportsWithOptions);
188+
}
179189

180190
bool FoundAnySideEffects = false;
181-
unsigned CurElem = RC.CurElem;
182191
bool Done;
183192
do {
184193
FoundAnySideEffects |=
185-
parseIntoSourceFile(SF, RC.CurBufferID, &Done, nullptr,
194+
parseIntoSourceFile(REPLInputFile, BufferID, &Done, nullptr,
186195
&PersistentState);
187-
performTypeChecking(SF, PersistentState.getTopLevelContext(), None,
188-
CurElem);
189-
CurElem = SF.Decls.size();
190196
} while (!Done);
191-
return FoundAnySideEffects;
197+
performTypeChecking(REPLInputFile, PersistentState.getTopLevelContext(),
198+
/*Options*/None);
199+
return REPLModule;
192200
}
193201

194202
/// An arbitrary, otherwise-unused char value that editline interprets as
@@ -327,8 +335,6 @@ class REPLInput {
327335
fflush(stdout);
328336
el_end(e);
329337
}
330-
331-
SourceFile &getREPLInputFile();
332338

333339
REPLInputKind getREPLInput(SmallVectorImpl<char> &Result) {
334340
ide::SourceCompleteResult SCR;
@@ -667,6 +673,8 @@ class REPLInput {
667673
if (trimmed > 0)
668674
llvm::outs() << " (and " << trimmed << " more)\n";
669675
}
676+
677+
SourceFile &getFileForCodeCompletion();
670678

671679
unsigned char onComplete(int ch) {
672680
const LineInfoW *line = el_wline(e);
@@ -677,7 +685,7 @@ class REPLInput {
677685

678686
if (!completions) {
679687
// If we aren't currently working with a completion set, generate one.
680-
completions.populate(getREPLInputFile(), Prefix);
688+
completions.populate(getFileForCodeCompletion(), Prefix);
681689
// Display the common root of the found completions and beep unless we
682690
// found a unique one.
683691
insertStringRef(completions.getRoot());
@@ -736,11 +744,7 @@ static void printOrDumpDecl(Decl *d, PrintOrDump which) {
736744
/// The compiler and execution environment for the REPL.
737745
class REPLEnvironment {
738746
CompilerInstance &CI;
739-
740-
public:
741-
SourceFile &REPLInputFile;
742-
743-
private:
747+
ModuleDecl *MostRecentModule;
744748
ProcessCmdLine CmdLine;
745749
llvm::SmallPtrSet<swift::ModuleDecl *, 8> ImportedModules;
746750
SmallVector<llvm::Function*, 8> InitFns;
@@ -757,8 +761,8 @@ class REPLEnvironment {
757761
const SILOptions SILOpts;
758762

759763
REPLInput Input;
760-
REPLContext RC;
761764
PersistentParserState PersistentState;
765+
unsigned NextLineNumber = 0;
762766

763767
private:
764768

@@ -858,17 +862,18 @@ class REPLEnvironment {
858862

859863
bool executeSwiftSource(llvm::StringRef Line, const ProcessCmdLine &CmdLine) {
860864
// Parse the current line(s).
861-
auto InputBuf = std::unique_ptr<llvm::MemoryBuffer>(
862-
llvm::MemoryBuffer::getMemBufferCopy(Line, "<REPL Input>"));
863-
bool ShouldRun = appendToREPLFile(REPLInputFile, PersistentState, RC,
864-
std::move(InputBuf));
865+
auto InputBuf = llvm::MemoryBuffer::getMemBufferCopy(Line, "<REPL Input>");
866+
SmallString<8> Name{"REPL_"};
867+
llvm::raw_svector_ostream(Name) << NextLineNumber;
868+
++NextLineNumber;
869+
ModuleDecl *M = typeCheckREPLInput(MostRecentModule, Name, PersistentState,
870+
std::move(InputBuf));
865871

866872
// SILGen the module and produce SIL diagnostics.
867873
std::unique_ptr<SILModule> sil;
868874

869875
if (!CI.getASTContext().hadError()) {
870-
sil = performSILGeneration(REPLInputFile, CI.getSILOptions(),
871-
RC.CurIRGenElem);
876+
sil = performSILGeneration(M, CI.getSILOptions());
872877
runSILDiagnosticPasses(*sil);
873878
runSILLoweringPasses(*sil);
874879
}
@@ -878,34 +883,23 @@ class REPLEnvironment {
878883
return false;
879884

880885
CI.getASTContext().Diags.resetHadAnyError();
881-
while (REPLInputFile.Decls.size() > RC.CurElem)
882-
REPLInputFile.Decls.pop_back();
883-
886+
884887
// FIXME: Handling of "import" declarations? Is there any other
885888
// state which needs to be reset?
886889

887890
return true;
888891
}
889-
890-
RC.CurElem = REPLInputFile.Decls.size();
891-
892+
MostRecentModule = M;
893+
892894
DumpSource += Line;
893-
894-
// If we didn't see an expression, statement, or decl which might have
895-
// side-effects, keep reading.
896-
if (!ShouldRun)
897-
return true;
898895

899-
const PrimarySpecificPaths PSPs =
900-
CI.getPrimarySpecificPathsForAtMostOnePrimary();
901896
// IRGen the current line(s).
902897
// FIXME: We shouldn't need to use the global context here, but
903898
// something is persisting across calls to performIRGeneration.
904899
auto LineModule = performIRGeneration(
905-
IRGenOpts, REPLInputFile, std::move(sil), "REPLLine", PSPs,
906-
getGlobalLLVMContext(), RC.CurIRGenElem);
907-
RC.CurIRGenElem = RC.CurElem;
908-
900+
IRGenOpts, M, std::move(sil), "REPLLine", PrimarySpecificPaths(),
901+
getGlobalLLVMContext(), /*parallelOutputFilenames*/{});
902+
909903
if (CI.getASTContext().hadError())
910904
return false;
911905

@@ -929,7 +923,7 @@ class REPLEnvironment {
929923
llvm::Function *DumpModuleMain = DumpModule.getFunction("main");
930924
DumpModuleMain->setName("repl.line");
931925

932-
if (autolinkImportedModules(getMainModule(), IRGenOpts))
926+
if (autolinkImportedModules(M, IRGenOpts))
933927
return false;
934928

935929
llvm::Module *TempModule = NewModule.get();
@@ -959,8 +953,7 @@ class REPLEnvironment {
959953
llvm::LLVMContext &LLVMCtx,
960954
bool ParseStdlib)
961955
: CI(CI),
962-
REPLInputFile(CI.getMainModule()->
963-
getMainSourceFile(SourceFileKind::REPL)),
956+
MostRecentModule(CI.getMainModule()),
964957
CmdLine(CmdLine),
965958
RanGlobalInitializers(false),
966959
LLVMContext(LLVMCtx),
@@ -969,20 +962,19 @@ class REPLEnvironment {
969962
IRGenOpts(),
970963
SILOpts(),
971964
Input(*this),
972-
RC{
973-
/*BufferID*/ 0U,
974-
/*CurElem*/ 0,
975-
/*CurIRGenElem*/ 0
976-
},
977965
PersistentState(CI.getASTContext())
978966
{
979967
ASTContext &Ctx = CI.getASTContext();
980-
if (!loadSwiftRuntime(Ctx.SearchPathOpts.RuntimeLibraryPath)) {
981-
CI.getDiags().diagnose(SourceLoc(),
982-
diag::error_immediate_mode_missing_stdlib);
983-
return;
968+
Ctx.LangOpts.EnableAccessControl = false;
969+
if (!ParseStdlib) {
970+
if (!loadSwiftRuntime(Ctx.SearchPathOpts.RuntimeLibraryPath)) {
971+
CI.getDiags().diagnose(SourceLoc(),
972+
diag::error_immediate_mode_missing_stdlib);
973+
return;
974+
}
975+
tryLoadLibraries(CI.getLinkLibraries(), Ctx.SearchPathOpts,
976+
CI.getDiags());
984977
}
985-
tryLoadLibraries(CI.getLinkLibraries(), Ctx.SearchPathOpts, CI.getDiags());
986978

987979
llvm::EngineBuilder builder{std::unique_ptr<llvm::Module>{Module}};
988980
std::string ErrorMsg;
@@ -1008,6 +1000,10 @@ class REPLEnvironment {
10081000
IRGenOpts.DebugInfoLevel = IRGenDebugInfoLevel::None;
10091001
IRGenOpts.DebugInfoFormat = IRGenDebugInfoFormat::None;
10101002

1003+
// The very first module is a dummy.
1004+
CI.getMainModule()->getMainSourceFile(SourceFileKind::REPL).ASTStage =
1005+
SourceFile::TypeChecked;
1006+
10111007
if (!ParseStdlib) {
10121008
// Force standard library to be loaded immediately. This forces any
10131009
// errors to appear upfront, and helps eliminate some nasty lag after the
@@ -1017,14 +1013,13 @@ class REPLEnvironment {
10171013
auto Buffer =
10181014
llvm::MemoryBuffer::getMemBufferCopy(WarmUpStmt,
10191015
"<REPL Initialization>");
1020-
appendToREPLFile(REPLInputFile, PersistentState, RC, std::move(Buffer));
1016+
(void)typeCheckREPLInput(MostRecentModule, "__Warmup", PersistentState,
1017+
std::move(Buffer));
10211018

10221019
if (Ctx.hadError())
10231020
return;
10241021
}
1025-
1026-
RC.CurElem = RC.CurIRGenElem = REPLInputFile.Decls.size();
1027-
1022+
10281023
if (llvm::sys::Process::StandardInIsUserInput())
10291024
llvm::outs() <<
10301025
"*** You are running Swift's integrated REPL, ***\n"
@@ -1034,13 +1029,14 @@ class REPLEnvironment {
10341029
"*** Type ':help' for assistance. ***\n";
10351030
}
10361031

1037-
swift::ModuleDecl *getMainModule() const {
1038-
return REPLInputFile.getParentModule();
1039-
}
10401032
StringRef getDumpSource() const { return DumpSource; }
10411033

10421034
/// Get the REPLInput object owned by the REPL instance.
10431035
REPLInput &getInput() { return Input; }
1036+
1037+
SourceFile &getFileForCodeCompletion() {
1038+
return MostRecentModule->getMainSourceFile(SourceFileKind::REPL);
1039+
}
10441040

10451041
/// Responds to a REPL input. Returns true if the repl should continue,
10461042
/// false if it should quit.
@@ -1070,8 +1066,6 @@ class REPLEnvironment {
10701066
" :constraints debug (on|off) - turn on/off the debug "
10711067
"output for the constraint-based type checker\n"
10721068
" :dump_ir - dump the LLVM IR generated by the REPL\n"
1073-
" :dump_ast - dump the AST representation of"
1074-
" the REPL input\n"
10751069
" :dump_decl <name> - dump the AST representation of the "
10761070
"named declarations\n"
10771071
" :dump_source - dump the user input (ignoring"
@@ -1086,17 +1080,17 @@ class REPLEnvironment {
10861080
return false;
10871081
} else if (L.peekNextToken().getText() == "dump_ir") {
10881082
DumpModule.print(llvm::dbgs(), nullptr, false, true);
1089-
} else if (L.peekNextToken().getText() == "dump_ast") {
1090-
REPLInputFile.dump();
10911083
} else if (L.peekNextToken().getText() == "dump_decl" ||
10921084
L.peekNextToken().getText() == "print_decl") {
10931085
PrintOrDump doPrint = (L.peekNextToken().getText() == "print_decl")
10941086
? PrintOrDump::Print : PrintOrDump::Dump;
10951087
L.lex(Tok);
10961088
L.lex(Tok);
10971089
ASTContext &ctx = CI.getASTContext();
1098-
UnqualifiedLookup lookup(ctx.getIdentifier(Tok.getText()),
1099-
&REPLInputFile, nullptr);
1090+
SourceFile &SF =
1091+
MostRecentModule->getMainSourceFile(SourceFileKind::REPL);
1092+
UnqualifiedLookup lookup(ctx.getIdentifier(Tok.getText()), &SF,
1093+
nullptr);
11001094
for (auto result : lookup.Results) {
11011095
printOrDumpDecl(result.getValueDecl(), doPrint);
11021096

@@ -1201,7 +1195,9 @@ class REPLEnvironment {
12011195
}
12021196
};
12031197

1204-
inline SourceFile &REPLInput::getREPLInputFile() { return Env.REPLInputFile; }
1198+
inline SourceFile &REPLInput::getFileForCodeCompletion() {
1199+
return Env.getFileForCodeCompletion();
1200+
}
12051201

12061202
void PrettyStackTraceREPL::print(llvm::raw_ostream &out) const {
12071203
out << "while processing REPL source:\n";

lib/Sema/TypeCheckREPL.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ void REPLChecker::processREPLTopLevelExpr(Expr *E) {
308308
E = TC.coerceToRValue(E);
309309

310310
// Create the meta-variable, let the typechecker name it.
311-
Identifier name = TC.getNextResponseVariableName(SF.getParentModule());
311+
Identifier name = TC.getNextResponseVariableName(&SF);
312312
VarDecl *vd = new (Context) VarDecl(/*IsStatic*/false,
313313
VarDecl::Specifier::Let,
314314
/*IsCaptureList*/false, E->getStartLoc(),
@@ -456,5 +456,6 @@ void TypeChecker::processREPLTopLevel(SourceFile &SF, TopLevelContext &TLC,
456456
}
457457

458458
contextualizeTopLevelCode(TLC, llvm::makeArrayRef(SF.Decls).slice(FirstDecl));
459+
SF.clearLookupCache();
459460
}
460461

test/Interpreter/SDK/Cocoa_repl.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ extension CGRect: Q {
2323
// non-imported types.
2424
struct Empty {}
2525
let _: Optional = Empty()
26-
// CHECK: Optional(REPL.Empty())
26+
// CHECK: Optional(REPL_{{.+}}.Empty())
2727
let _: Optional = CGPoint.zero
2828
// CHECK: Optional((0.0, 0.0))
2929
let _: Optional = NSString.availableStringEncodings

test/Interpreter/repl.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,17 +157,17 @@ var b = a.slice[3..<5]
157157

158158
struct Inner<T> {}
159159
struct Outer<T> { var inner : Inner<T> }
160-
Outer<Int>(inner: Inner()) // CHECK: Outer<Int> = REPL.Outer
160+
Outer<Int>(inner: Inner()) // CHECK: Outer<Int> = REPL_{{.+}}.Outer
161161

162162
struct ContainsSlice { var slice : [Int] }
163-
ContainsSlice(slice: [1, 2, 3]) // CHECK: ContainsSlice = REPL.ContainsSlice
163+
ContainsSlice(slice: [1, 2, 3]) // CHECK: ContainsSlice = REPL_{{.+}}.ContainsSlice
164164

165165
struct ContainsGenericSlice<T> { var slice : [T] }
166-
ContainsGenericSlice(slice: [1, 2, 3]) // CHECK: ContainsGenericSlice<Int> = REPL.ContainsGenericSlice
167-
ContainsGenericSlice(slice: [(1, 2), (3, 4)]) // CHECK: ContainsGenericSlice<(Int, Int)> = REPL.ContainsGenericSlice
166+
ContainsGenericSlice(slice: [1, 2, 3]) // CHECK: ContainsGenericSlice<Int> = REPL_{{.+}}.ContainsGenericSlice
167+
ContainsGenericSlice(slice: [(1, 2), (3, 4)]) // CHECK: ContainsGenericSlice<(Int, Int)> = REPL_{{.+}}.ContainsGenericSlice
168168

169169
struct ContainsContainsSlice { var containsSlice : ContainsSlice }
170-
ContainsContainsSlice(containsSlice: ContainsSlice(slice: [1, 2, 3])) // CHECK: ContainsContainsSlice = REPL.ContainsContainsSlice
170+
ContainsContainsSlice(containsSlice: ContainsSlice(slice: [1, 2, 3])) // CHECK: ContainsContainsSlice = REPL_{{.+}}.ContainsContainsSlice
171171

172172
protocol Proto {
173173
func foo()

0 commit comments

Comments
 (0)