Coverage Report

Created: 2023-09-21 18:56

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Interpreter/IncrementalParser.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--------- IncrementalParser.cpp - Incremental Compilation  -----------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// This file implements the class which performs incremental code compilation.
10
//
11
//===----------------------------------------------------------------------===//
12
13
#include "IncrementalParser.h"
14
15
#include "clang/AST/DeclContextInternals.h"
16
#include "clang/CodeGen/BackendUtil.h"
17
#include "clang/CodeGen/CodeGenAction.h"
18
#include "clang/CodeGen/ModuleBuilder.h"
19
#include "clang/Frontend/CompilerInstance.h"
20
#include "clang/Frontend/FrontendAction.h"
21
#include "clang/FrontendTool/Utils.h"
22
#include "clang/Interpreter/Interpreter.h"
23
#include "clang/Parse/Parser.h"
24
#include "clang/Sema/Sema.h"
25
#include "llvm/Option/ArgList.h"
26
#include "llvm/Support/CrashRecoveryContext.h"
27
#include "llvm/Support/Error.h"
28
#include "llvm/Support/Timer.h"
29
30
#include <sstream>
31
32
namespace clang {
33
34
class IncrementalASTConsumer final : public ASTConsumer {
35
  Interpreter &Interp;
36
  std::unique_ptr<ASTConsumer> Consumer;
37
38
public:
39
  IncrementalASTConsumer(Interpreter &InterpRef, std::unique_ptr<ASTConsumer> C)
40
50
      : Interp(InterpRef), Consumer(std::move(C)) {}
41
42
583
  bool HandleTopLevelDecl(DeclGroupRef DGR) override final {
43
583
    if (DGR.isNull())
44
0
      return true;
45
583
    if (!Consumer)
46
0
      return true;
47
48
583
    for (Decl *D : DGR)
49
588
      if (auto *TSD = llvm::dyn_cast<TopLevelStmtDecl>(D);
50
588
          TSD && 
TSD->isSemiMissing()27
)
51
9
        TSD->setStmt(Interp.SynthesizeExpr(cast<Expr>(TSD->getStmt())));
52
53
583
    return Consumer->HandleTopLevelDecl(DGR);
54
583
  }
55
412
  void HandleTranslationUnit(ASTContext &Ctx) override final {
56
412
    Consumer->HandleTranslationUnit(Ctx);
57
412
  }
58
0
  void HandleInlineFunctionDefinition(FunctionDecl *D) override final {
59
0
    Consumer->HandleInlineFunctionDefinition(D);
60
0
  }
61
0
  void HandleInterestingDecl(DeclGroupRef D) override final {
62
0
    Consumer->HandleInterestingDecl(D);
63
0
  }
64
0
  void HandleTagDeclDefinition(TagDecl *D) override final {
65
0
    Consumer->HandleTagDeclDefinition(D);
66
0
  }
67
0
  void HandleTagDeclRequiredDefinition(const TagDecl *D) override final {
68
0
    Consumer->HandleTagDeclRequiredDefinition(D);
69
0
  }
70
0
  void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override final {
71
0
    Consumer->HandleCXXImplicitFunctionInstantiation(D);
72
0
  }
73
0
  void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override final {
74
0
    Consumer->HandleTopLevelDeclInObjCContainer(D);
75
0
  }
76
0
  void HandleImplicitImportDecl(ImportDecl *D) override final {
77
0
    Consumer->HandleImplicitImportDecl(D);
78
0
  }
79
0
  void CompleteTentativeDefinition(VarDecl *D) override final {
80
0
    Consumer->CompleteTentativeDefinition(D);
81
0
  }
82
0
  void CompleteExternalDeclaration(VarDecl *D) override final {
83
0
    Consumer->CompleteExternalDeclaration(D);
84
0
  }
85
0
  void AssignInheritanceModel(CXXRecordDecl *RD) override final {
86
0
    Consumer->AssignInheritanceModel(RD);
87
0
  }
88
0
  void HandleCXXStaticMemberVarInstantiation(VarDecl *D) override final {
89
0
    Consumer->HandleCXXStaticMemberVarInstantiation(D);
90
0
  }
91
0
  void HandleVTable(CXXRecordDecl *RD) override final {
92
0
    Consumer->HandleVTable(RD);
93
0
  }
94
0
  ASTMutationListener *GetASTMutationListener() override final {
95
0
    return Consumer->GetASTMutationListener();
96
0
  }
97
0
  ASTDeserializationListener *GetASTDeserializationListener() override final {
98
0
    return Consumer->GetASTDeserializationListener();
99
0
  }
100
0
  void PrintStats() override final { Consumer->PrintStats(); }
101
0
  bool shouldSkipFunctionBody(Decl *D) override final {
102
0
    return Consumer->shouldSkipFunctionBody(D);
103
0
  }
104
0
  static bool classof(const clang::ASTConsumer *) { return true; }
105
};
106
107
/// A custom action enabling the incremental processing functionality.
108
///
109
/// The usual \p FrontendAction expects one call to ExecuteAction and once it
110
/// sees a call to \p EndSourceFile it deletes some of the important objects
111
/// such as \p Preprocessor and \p Sema assuming no further input will come.
112
///
113
/// \p IncrementalAction ensures it keep its underlying action's objects alive
114
/// as long as the \p IncrementalParser needs them.
115
///
116
class IncrementalAction : public WrapperFrontendAction {
117
private:
118
  bool IsTerminating = false;
119
120
public:
121
  IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx,
122
                    llvm::Error &Err)
123
50
      : WrapperFrontendAction([&]() {
124
50
          llvm::ErrorAsOutParameter EAO(&Err);
125
50
          std::unique_ptr<FrontendAction> Act;
126
50
          switch (CI.getFrontendOpts().ProgramAction) {
127
0
          default:
128
0
            Err = llvm::createStringError(
129
0
                std::errc::state_not_recoverable,
130
0
                "Driver initialization failed. "
131
0
                "Incremental mode for action %d is not supported",
132
0
                CI.getFrontendOpts().ProgramAction);
133
0
            return Act;
134
1
          case frontend::ASTDump:
135
1
            [[fallthrough]];
136
1
          case frontend::ASTPrint:
137
1
            [[fallthrough]];
138
1
          case frontend::ParseSyntaxOnly:
139
1
            Act = CreateFrontendAction(CI);
140
1
            break;
141
0
          case frontend::PluginAction:
142
0
            [[fallthrough]];
143
7
          case frontend::EmitAssembly:
144
7
            [[fallthrough]];
145
8
          case frontend::EmitBC:
146
8
            [[fallthrough]];
147
38
          case frontend::EmitObj:
148
38
            [[fallthrough]];
149
39
          case frontend::PrintPreprocessedInput:
150
39
            [[fallthrough]];
151
49
          case frontend::EmitLLVMOnly:
152
49
            Act.reset(new EmitLLVMOnlyAction(&LLVMCtx));
153
49
            break;
154
50
          }
155
50
          return Act;
156
50
        }()) {}
157
450
  FrontendAction *getWrapped() const { return WrappedAction.get(); }
158
100
  TranslationUnitKind getTranslationUnitKind() override {
159
100
    return TU_Incremental;
160
100
  }
161
162
50
  void ExecuteAction() override {
163
50
    CompilerInstance &CI = getCompilerInstance();
164
50
    assert(CI.hasPreprocessor() && "No PP!");
165
166
    // Use a code completion consumer?
167
50
    CodeCompleteConsumer *CompletionConsumer = nullptr;
168
50
    if (CI.hasCodeCompletionConsumer())
169
0
      CompletionConsumer = &CI.getCodeCompletionConsumer();
170
171
50
    Preprocessor &PP = CI.getPreprocessor();
172
50
    PP.EnterMainSourceFile();
173
174
50
    if (!CI.hasSema())
175
50
      CI.createSema(getTranslationUnitKind(), CompletionConsumer);
176
50
  }
177
178
  // Do not terminate after processing the input. This allows us to keep various
179
  // clang objects alive and to incrementally grow the current TU.
180
86
  void EndSourceFile() override {
181
    // The WrappedAction can be nullptr if we issued an error in the ctor.
182
86
    if (IsTerminating && 
getWrapped()36
)
183
36
      WrapperFrontendAction::EndSourceFile();
184
86
  }
185
186
36
  void FinalizeAction() {
187
36
    assert(!IsTerminating && "Already finalized!");
188
36
    IsTerminating = true;
189
36
    EndSourceFile();
190
36
  }
191
};
192
193
414
CodeGenerator *IncrementalParser::getCodeGen() const {
194
414
  FrontendAction *WrappedAct = Act->getWrapped();
195
414
  if (!WrappedAct->hasIRSupport())
196
16
    return nullptr;
197
398
  return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
198
414
}
199
200
0
IncrementalParser::IncrementalParser() {}
201
202
IncrementalParser::IncrementalParser(Interpreter &Interp,
203
                                     std::unique_ptr<CompilerInstance> Instance,
204
                                     llvm::LLVMContext &LLVMCtx,
205
                                     llvm::Error &Err)
206
50
    : CI(std::move(Instance)) {
207
50
  llvm::ErrorAsOutParameter EAO(&Err);
208
50
  Act = std::make_unique<IncrementalAction>(*CI, LLVMCtx, Err);
209
50
  if (Err)
210
0
    return;
211
50
  CI->ExecuteAction(*Act);
212
50
  std::unique_ptr<ASTConsumer> IncrConsumer =
213
50
      std::make_unique<IncrementalASTConsumer>(Interp, CI->takeASTConsumer());
214
50
  CI->setASTConsumer(std::move(IncrConsumer));
215
50
  Consumer = &CI->getASTConsumer();
216
50
  P.reset(
217
50
      new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false));
218
50
  P->Initialize();
219
220
  // An initial PTU is needed as CUDA includes some headers automatically
221
50
  auto PTU = ParseOrWrapTopLevelDecl();
222
50
  if (auto E = PTU.takeError()) {
223
0
    consumeError(std::move(E)); // FIXME
224
0
    return;                     // PTU.takeError();
225
0
  }
226
227
50
  if (CodeGenerator *CG = getCodeGen()) {
228
49
    std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
229
49
    CG->StartModule("incr_module_" + std::to_string(PTUs.size()),
230
49
                    M->getContext());
231
49
    PTU->TheModule = std::move(M);
232
49
    assert(PTU->TheModule && "Failed to create initial PTU");
233
49
  }
234
50
}
235
236
36
IncrementalParser::~IncrementalParser() {
237
36
  P.reset();
238
36
  Act->FinalizeAction();
239
36
}
240
241
llvm::Expected<PartialTranslationUnit &>
242
416
IncrementalParser::ParseOrWrapTopLevelDecl() {
243
  // Recover resources if we crash before exiting this method.
244
416
  Sema &S = CI->getSema();
245
416
  llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S);
246
416
  Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true);
247
416
  Sema::LocalEagerInstantiationScope LocalInstantiations(S);
248
249
416
  PTUs.emplace_back(PartialTranslationUnit());
250
416
  PartialTranslationUnit &LastPTU = PTUs.back();
251
  // Add a new PTU.
252
416
  ASTContext &C = S.getASTContext();
253
416
  C.addTranslationUnitDecl();
254
416
  LastPTU.TUPart = C.getTranslationUnitDecl();
255
256
  // Skip previous eof due to last incremental input.
257
416
  if (P->getCurToken().is(tok::annot_repl_input_end)) {
258
416
    P->ConsumeAnyToken();
259
    // FIXME: Clang does not call ExitScope on finalizing the regular TU, we
260
    // might want to do that around HandleEndOfTranslationUnit.
261
416
    P->ExitScope();
262
416
    S.CurContext = nullptr;
263
    // Start a new PTU.
264
416
    P->EnterScope(Scope::DeclScope);
265
416
    S.ActOnTranslationUnitScope(P->getCurScope());
266
416
  }
267
268
416
  Parser::DeclGroupPtrTy ADecl;
269
416
  Sema::ModuleImportState ImportState;
270
1.00k
  for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF;
271
584
       AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) {
272
584
    if (ADecl && 
!Consumer->HandleTopLevelDecl(ADecl.get())582
)
273
0
      return llvm::make_error<llvm::StringError>("Parsing failed. "
274
0
                                                 "The consumer rejected a decl",
275
0
                                                 std::error_code());
276
584
  }
277
278
416
  DiagnosticsEngine &Diags = getCI()->getDiagnostics();
279
416
  if (Diags.hasErrorOccurred()) {
280
4
    PartialTranslationUnit MostRecentPTU = {C.getTranslationUnitDecl(),
281
4
                                            nullptr};
282
4
    CleanUpPTU(MostRecentPTU);
283
284
4
    Diags.Reset(/*soft=*/true);
285
4
    Diags.getClient()->clear();
286
4
    return llvm::make_error<llvm::StringError>("Parsing failed.",
287
4
                                               std::error_code());
288
4
  }
289
290
  // Process any TopLevelDecls generated by #pragma weak.
291
412
  for (Decl *D : S.WeakTopLevelDecls()) {
292
0
    DeclGroupRef DGR(D);
293
0
    Consumer->HandleTopLevelDecl(DGR);
294
0
  }
295
296
412
  LocalInstantiations.perform();
297
412
  GlobalInstantiations.perform();
298
299
412
  Consumer->HandleTranslationUnit(C);
300
301
412
  return LastPTU;
302
416
}
303
304
llvm::Expected<PartialTranslationUnit &>
305
366
IncrementalParser::Parse(llvm::StringRef input) {
306
366
  Preprocessor &PP = CI->getPreprocessor();
307
366
  assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");
308
309
366
  std::ostringstream SourceName;
310
366
  SourceName << "input_line_" << InputCount++;
311
312
  // Create an uninitialized memory buffer, copy code in and append "\n"
313
366
  size_t InputSize = input.size(); // don't include trailing 0
314
  // MemBuffer size should *not* include terminating zero
315
366
  std::unique_ptr<llvm::MemoryBuffer> MB(
316
366
      llvm::WritableMemoryBuffer::getNewUninitMemBuffer(InputSize + 1,
317
366
                                                        SourceName.str()));
318
366
  char *MBStart = const_cast<char *>(MB->getBufferStart());
319
366
  memcpy(MBStart, input.data(), InputSize);
320
366
  MBStart[InputSize] = '\n';
321
322
366
  SourceManager &SM = CI->getSourceManager();
323
324
  // FIXME: Create SourceLocation, which will allow clang to order the overload
325
  // candidates for example
326
366
  SourceLocation NewLoc = SM.getLocForStartOfFile(SM.getMainFileID());
327
328
  // Create FileID for the current buffer.
329
366
  FileID FID = SM.createFileID(std::move(MB), SrcMgr::C_User, /*LoadedID=*/0,
330
366
                               /*LoadedOffset=*/0, NewLoc);
331
332
  // NewLoc only used for diags.
333
366
  if (PP.EnterSourceFile(FID, /*DirLookup=*/nullptr, NewLoc))
334
0
    return llvm::make_error<llvm::StringError>("Parsing failed. "
335
0
                                               "Cannot enter source file.",
336
0
                                               std::error_code());
337
338
366
  auto PTU = ParseOrWrapTopLevelDecl();
339
366
  if (!PTU)
340
4
    return PTU.takeError();
341
342
362
  if (PP.getLangOpts().DelayedTemplateParsing) {
343
    // Microsoft-specific:
344
    // Late parsed templates can leave unswallowed "macro"-like tokens.
345
    // They will seriously confuse the Parser when entering the next
346
    // source file. So lex until we are EOF.
347
0
    Token Tok;
348
0
    do {
349
0
      PP.Lex(Tok);
350
0
    } while (Tok.isNot(tok::annot_repl_input_end));
351
362
  } else {
352
362
    Token AssertTok;
353
362
    PP.Lex(AssertTok);
354
362
    assert(AssertTok.is(tok::annot_repl_input_end) &&
355
362
           "Lexer must be EOF when starting incremental parse!");
356
362
  }
357
358
362
  if (std::unique_ptr<llvm::Module> M = GenModule())
359
347
    PTU->TheModule = std::move(M);
360
361
362
  return PTU;
362
362
}
363
364
362
std::unique_ptr<llvm::Module> IncrementalParser::GenModule() {
365
362
  static unsigned ID = 0;
366
362
  if (CodeGenerator *CG = getCodeGen()) {
367
347
    std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
368
347
    CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());
369
347
    return M;
370
347
  }
371
15
  return nullptr;
372
362
}
373
374
10
void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) {
375
10
  TranslationUnitDecl *MostRecentTU = PTU.TUPart;
376
10
  TranslationUnitDecl *FirstTU = MostRecentTU->getFirstDecl();
377
10
  if (StoredDeclsMap *Map = FirstTU->getPrimaryContext()->getLookupPtr()) {
378
171
    for (auto I = Map->begin(); I != Map->end(); 
++I161
) {
379
161
      StoredDeclsList &List = I->second;
380
161
      DeclContextLookupResult R = List.getLookupResult();
381
271
      for (NamedDecl *D : R) {
382
271
        if (D->getTranslationUnitDecl() == MostRecentTU) {
383
8
          List.remove(D);
384
8
        }
385
271
      }
386
161
      if (List.isNull())
387
8
        Map->erase(I);
388
161
    }
389
10
  }
390
10
}
391
392
2
llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const {
393
2
  CodeGenerator *CG = getCodeGen();
394
2
  assert(CG);
395
2
  return CG->GetMangledName(GD);
396
2
}
397
} // end namespace clang