Basic support C++20 named module. (#12)

This commit is contained in:
ykiko
2024-12-14 13:40:13 +08:00
committed by GitHub
parent 7d42b4e1ec
commit 5326480cd6
30 changed files with 1082 additions and 593 deletions

View File

@@ -19,7 +19,7 @@ bool PCHInfo::needUpdate(llvm::StringRef content) {
namespace {
auto createInvocation(CompliationParams& params) {
auto createInvocation(CompilationParams& params) {
llvm::SmallString<1024> buffer;
llvm::SmallVector<const char*, 16> args;
@@ -45,7 +45,7 @@ auto createInvocation(CompliationParams& params) {
return invocation;
}
auto createInstance(CompliationParams& params) {
auto createInstance(CompilationParams& params) {
auto instance = std::make_unique<clang::CompilerInstance>();
instance->setInvocation(createInvocation(params));
@@ -64,10 +64,12 @@ auto createInstance(CompliationParams& params) {
assert(!instance->getPreprocessorOpts().RetainRemappedFileBuffers &&
"RetainRemappedFileBuffers should be false");
instance->getPreprocessorOpts().addRemappedFile(
params.srcPath,
llvm::MemoryBuffer::getMemBufferCopy(params.content.substr(0, size), params.srcPath)
.release());
if(!params.content.empty()) {
instance->getPreprocessorOpts().addRemappedFile(
params.srcPath,
llvm::MemoryBuffer::getMemBufferCopy(params.content.substr(0, size), params.srcPath)
.release());
}
for(auto& [file, content]: params.remappedFiles) {
instance->getPreprocessorOpts().addRemappedFile(
@@ -75,14 +77,19 @@ auto createInstance(CompliationParams& params) {
llvm::MemoryBuffer::getMemBufferCopy(content, file).release());
}
if(!instance->createTarget()) {
/// FIXME: add error handle here.
std::terminate();
}
return instance;
}
void applyPreamble(clang::CompilerInstance& instance, CompliationParams& params) {
void applyPreamble(clang::CompilerInstance& instance, CompilationParams& params) {
auto& PPOpts = instance.getPreprocessorOpts();
auto& pch = params.pch;
auto& bounds = params.pchBounds;
auto& pcms = params.pcms;
if(bounds.Size != 0) {
PPOpts.UsePredefines = false;
PPOpts.ImplicitPCHInclude = std::move(pch);
@@ -91,30 +98,34 @@ void applyPreamble(clang::CompilerInstance& instance, CompliationParams& params)
PPOpts.DisablePCHOrModuleValidation = clang::DisableValidationForModuleKind::PCH;
}
auto& pcms = params.pcms;
for(auto& [name, path]: pcms) {
auto& HSOpts = instance.getHeaderSearchOpts();
HSOpts.PrebuiltModuleFiles.try_emplace(std::move(name), std::move(path));
HSOpts.PrebuiltModuleFiles.try_emplace(name.str(), std::move(path));
}
}
llvm::Expected<ASTInfo> ExecuteAction(std::unique_ptr<clang::CompilerInstance> instance,
clang::frontend::ActionKind kind) {
std::unique_ptr<clang::ASTFrontendAction> action;
if(kind == clang::frontend::ActionKind::ParseSyntaxOnly) {
action = std::make_unique<clang::SyntaxOnlyAction>();
} else if(kind == clang::frontend::ActionKind::GeneratePCH) {
action = std::make_unique<clang::GeneratePCHAction>();
} else if(kind == clang::frontend::ActionKind::GenerateReducedModuleInterface) {
action = std::make_unique<clang::GenerateReducedModuleInterfaceAction>();
} else {
llvm::errs() << "Unsupported action kind\n";
std::terminate();
/// Execute given action with the on the given instance. `callback` is called after
/// `BeginSourceFile`. Beacuse `BeginSourceFile` may create new preprocessor.
llvm::Error ExecuteAction(clang::CompilerInstance& instance,
clang::FrontendAction& action,
auto&& callback) {
if(!action.BeginSourceFile(instance, instance.getFrontendOpts().Inputs[0])) {
return error("Failed to begin source file");
}
if(!instance->createTarget()) {
return error("Failed to create target");
callback();
if(auto error = action.Execute()) {
return error;
}
return llvm::Error::success();
}
llvm::Expected<ASTInfo> ExecuteAction(std::unique_ptr<clang::CompilerInstance> instance,
std::unique_ptr<clang::FrontendAction> action) {
if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) {
return error("Failed to begin source file");
}
@@ -129,12 +140,13 @@ llvm::Expected<ASTInfo> ExecuteAction(std::unique_ptr<clang::CompilerInstance> i
llvm::DenseMap<clang::FileID, Directive> directives;
Directive::attach(pp, directives);
std::optional<clang::syntax::TokenCollector> collector;
/// Collect tokens.
std::optional<clang::syntax::TokenCollector> tokCollector;
/// It is not necessary to collect tokens if we are running code completion.
/// And in fact will cause assertion failure.
if(!instance->hasCodeCompletionConsumer()) {
collector.emplace(pp);
tokCollector.emplace(pp);
}
if(auto error = action->Execute()) {
@@ -142,19 +154,23 @@ llvm::Expected<ASTInfo> ExecuteAction(std::unique_ptr<clang::CompilerInstance> i
}
std::unique_ptr<clang::syntax::TokenBuffer> tokBuf;
if(collector) {
tokBuf = std::make_unique<clang::syntax::TokenBuffer>(std::move(*collector).consume());
if(tokCollector) {
tokBuf = std::make_unique<clang::syntax::TokenBuffer>(std::move(*tokCollector).consume());
}
/// FIXME: getDependencies currently return ArrayRef<std::string>, which actually results in
/// extra copy. It would be great to avoid this copy.
return ASTInfo(std::move(action),
std::move(instance),
std::move(tokBuf),
std::move(directives));
std::move(directives),
{});
}
} // namespace
void CompliationParams::computeBounds(llvm::StringRef header) {
void CompilationParams::computeBounds(llvm::StringRef header) {
assert(!bounds.has_value() && "Bounds is already computed");
assert(!content.empty() && "Source content is required to compute bounds");
@@ -248,15 +264,101 @@ void CompliationParams::computeBounds(llvm::StringRef header) {
}
}
llvm::Expected<ASTInfo> compile(CompliationParams& params) {
/// Scan the module name. This will not run the preprocessor.
std::string scanModuleName(llvm::StringRef content) {
clang::LangOptions langOpts;
langOpts.Modules = true;
langOpts.CPlusPlus20 = true;
clang::Lexer lexer(clang::SourceLocation(),
langOpts,
content.begin(),
content.begin(),
content.end());
clang::Token token;
lexer.Lex(token);
while(!token.is(clang::tok::eof)) {
llvm::outs() << token.getName() << "\n";
if(token.is(clang::tok::kw_module)) {
lexer.Lex(token);
if(token.is(clang::tok::coloncolon)) {
lexer.Lex(token);
}
if(token.is(clang::tok::identifier)) {
return token.getIdentifierInfo()->getName().str();
}
}
lexer.Lex(token);
}
return "";
}
llvm::Expected<ModuleInfo> scanModule(CompilationParams& params) {
struct ModuleCollector : public clang::PPCallbacks {
ModuleInfo& info;
ModuleCollector(ModuleInfo& info) : info(info) {}
void moduleImport(clang::SourceLocation importLoc,
clang::ModuleIdPath path,
const clang::Module* imported) override {
assert(path.size() == 1);
info.mods.emplace_back(path[0].first->getName());
}
};
ModuleInfo info;
clang::PreprocessOnlyAction action;
auto instance = createInstance(params);
if(!action.BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) {
return error("Failed to begin source file");
}
auto& pp = instance->getPreprocessor();
pp.addPPCallbacks(std::make_unique<ModuleCollector>(info));
if(auto error = action.Execute()) {
return error;
}
if(pp.isInNamedModule()) {
info.isInterfaceUnit = pp.isInNamedInterfaceUnit();
info.name = pp.getNamedModuleName();
}
return info;
}
llvm::Expected<ASTInfo> compile(CompilationParams& params) {
auto instance = createInstance(params);
applyPreamble(*instance, params);
return ExecuteAction(std::move(instance), clang::frontend::ActionKind::ParseSyntaxOnly);
return ExecuteAction(std::move(instance), std::make_unique<clang::SyntaxOnlyAction>());
}
llvm::Expected<ASTInfo> compile(CompliationParams& params, PCHInfo& out) {
llvm::Expected<ASTInfo> compile(CompilationParams& params, clang::CodeCompleteConsumer* consumer) {
auto instance = createInstance(params);
/// Set options to run code completion.
instance->getFrontendOpts().CodeCompletionAt.FileName = params.srcPath.str();
instance->getFrontendOpts().CodeCompletionAt.Line = params.line;
instance->getFrontendOpts().CodeCompletionAt.Column = params.column;
instance->setCodeCompletionConsumer(consumer);
applyPreamble(*instance, params);
return ExecuteAction(std::move(instance), std::make_unique<clang::SyntaxOnlyAction>());
}
llvm::Expected<ASTInfo> compile(CompilationParams& params, PCHInfo& out) {
assert(params.bounds.has_value() && "Preamble bounds is required to build PCH");
auto instance = createInstance(params);
@@ -268,25 +370,27 @@ llvm::Expected<ASTInfo> compile(CompliationParams& params, PCHInfo& out) {
instance->getPreprocessorOpts().GeneratePreamble = true;
instance->getLangOpts().CompilingPCH = true;
if(auto info = ExecuteAction(std::move(instance), clang::frontend::ActionKind::GeneratePCH)) {
out.path = params.outPath.str();
out.srcPath = params.srcPath.str();
auto& bounds = *params.bounds;
out.preamble = params.content.substr(0, bounds.Size).str();
if(bounds.PreambleEndsAtStartOfLine) {
out.preamble.append("@");
}
/// TODO: collect files involved in building this PCH.
return std::move(*info);
} else {
auto info = ExecuteAction(std::move(instance), std::make_unique<clang::GeneratePCHAction>());
if(!info) {
return info.takeError();
}
out.path = params.outPath.str();
out.srcPath = params.srcPath.str();
auto& bounds = *params.bounds;
out.preamble = params.content.substr(0, bounds.Size).str();
out.deps = info->deps();
if(bounds.PreambleEndsAtStartOfLine) {
out.preamble.append("@");
}
/// TODO: collect files involved in building this PCH.
return std::move(*info);
}
llvm::Expected<ASTInfo> compile(CompliationParams& params, PCMInfo& out) {
llvm::Expected<ASTInfo> compile(CompilationParams& params, PCMInfo& out) {
auto instance = createInstance(params);
/// Set options to generate PCM.
@@ -295,29 +399,26 @@ llvm::Expected<ASTInfo> compile(CompliationParams& params, PCMInfo& out) {
applyPreamble(*instance, params);
if(auto info = ExecuteAction(std::move(instance),
clang::frontend::ActionKind::GenerateReducedModuleInterface)) {
out.path = params.outPath.str();
out.name = info->context().getCurrentNamedModule()->Name;
return std::move(*info);
} else {
auto info = ExecuteAction(std::move(instance),
std::make_unique<clang::GenerateReducedModuleInterfaceAction>());
if(!info) {
return info.takeError();
}
}
llvm::Expected<ASTInfo> compile(CompliationParams& params, clang::CodeCompleteConsumer* consumer) {
auto instance = createInstance(params);
assert(info->pp().isInNamedInterfaceUnit() &&
"Only module interface unit could be built as PCM");
/// Set options to run code completion.
instance->getFrontendOpts().CodeCompletionAt.FileName = params.srcPath.str();
instance->getFrontendOpts().CodeCompletionAt.Line = params.line;
instance->getFrontendOpts().CodeCompletionAt.Column = params.column;
instance->setCodeCompletionConsumer(consumer);
out.isInterfaceUnit = true;
out.name = info->pp().getNamedModuleName();
for(auto& [name, path]: params.pcms) {
out.mods.emplace_back(name);
}
applyPreamble(*instance, params);
out.path = params.outPath.str();
out.srcPath = params.srcPath.str();
out.deps = info->deps();
return ExecuteAction(std::move(instance), clang::frontend::ActionKind::ParseSyntaxOnly);
return std::move(*info);
}
} // namespace clice