Skip to content

[C++20][Modules] Implement P1857R3 Modules Dependency Discovery #107168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion clang/examples/AnnotateFunctions/AnnotateFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class PragmaAnnotateHandler : public PragmaHandler {
Token Tok;
PP.LexUnexpandedToken(Tok);
if (Tok.isNot(tok::eod))
PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "#pragma";

if (HandledDecl) {
DiagnosticsEngine &D = PP.getDiagnostics();
Expand Down
20 changes: 18 additions & 2 deletions clang/include/clang/Basic/DiagnosticLexKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,9 @@ def err_pp_embed_device_file : Error<

def ext_pp_extra_tokens_at_eol : ExtWarn<
"extra tokens at end of #%0 directive">, InGroup<ExtraTokens>;
def ext_pp_extra_tokens_at_module_directive_eol
: ExtWarn<"extra tokens at end of '%0' directive">,
InGroup<ExtraTokens>;

def ext_pp_comma_expr : Extension<"comma operator in operand of #if">;
def ext_pp_bad_vaargs_use : Extension<
Expand Down Expand Up @@ -495,8 +498,8 @@ def warn_cxx98_compat_variadic_macro : Warning<
InGroup<CXX98CompatPedantic>, DefaultIgnore;
def ext_named_variadic_macro : Extension<
"named variadic macros are a GNU extension">, InGroup<VariadicMacros>;
def err_embedded_directive : Error<
"embedding a #%0 directive within macro arguments is not supported">;
def err_embedded_directive : Error<"embedding a %select{#|C++ }0%1 directive "
"within macro arguments is not supported">;
def ext_embedded_directive : Extension<
"embedding a directive within macro arguments has undefined behavior">,
InGroup<DiagGroup<"embedded-directive">>;
Expand Down Expand Up @@ -983,6 +986,19 @@ def warn_module_conflict : Warning<
InGroup<ModuleConflict>;

// C++20 modules
def err_pp_expected_module_name_or_header_name
: Error<"expected module name or header name">;
def err_pp_expected_semi_after_module_or_import
: Error<"'%select{module|import}0' directive must end with a ';' on the "
"same line">;
def err_module_decl_in_header
: Error<"module declaration must not come from an #include directive">;
def err_pp_cond_span_module_decl
: Error<"preprocessor conditionals shall not span a module declaration">;
def err_pp_module_expected_ident
: Error<"expected a module name after '%select{module|import}0'">;
def err_pp_unsupported_module_partition
: Error<"module partitions are only supported for C++20 onwards">;
def err_header_import_semi_in_macro : Error<
"semicolon terminating header import declaration cannot be produced "
"by a macro">;
Expand Down
62 changes: 30 additions & 32 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1762,38 +1762,36 @@ def ext_bit_int : Extension<
} // end of Parse Issue category.

let CategoryName = "Modules Issue" in {
def err_unexpected_module_decl : Error<
"module declaration can only appear at the top level">;
def err_module_expected_ident : Error<
"expected a module name after '%select{module|import}0'">;
def err_attribute_not_module_attr : Error<
"%0 attribute cannot be applied to a module">;
def err_keyword_not_module_attr : Error<
"%0 cannot be applied to a module">;
def err_attribute_not_import_attr : Error<
"%0 attribute cannot be applied to a module import">;
def err_keyword_not_import_attr : Error<
"%0 cannot be applied to a module import">;
def err_module_expected_semi : Error<
"expected ';' after module name">;
def err_global_module_introducer_not_at_start : Error<
"'module;' introducing a global module fragment can appear only "
"at the start of the translation unit">;
def err_module_fragment_exported : Error<
"%select{global|private}0 module fragment cannot be exported">;
def err_private_module_fragment_expected_semi : Error<
"expected ';' after private module fragment declaration">;
def err_missing_before_module_end : Error<"expected %0 at end of module">;
def err_unsupported_module_partition : Error<
"module partitions are only supported for C++20 onwards">;
def err_import_not_allowed_here : Error<
"imports must immediately follow the module declaration">;
def err_partition_import_outside_module : Error<
"module partition imports must be within a module purview">;
def err_import_in_wrong_fragment : Error<
"module%select{| partition}0 imports cannot be in the %select{global|private}1 module fragment">;

def err_export_empty : Error<"export declaration cannot be empty">;
def err_unexpected_module_import_decl
: Error<"%select{module|import}0 declaration can only appear at the top "
"level">;
def err_module_expected_ident
: Error<"expected a module name after '%select{module|import}0'">;
def err_attribute_not_module_attr
: Error<"%0 attribute cannot be applied to a module">;
def err_keyword_not_module_attr : Error<"%0 cannot be applied to a module">;
def err_attribute_not_import_attr
: Error<"%0 attribute cannot be applied to a module import">;
def err_keyword_not_import_attr
: Error<"%0 cannot be applied to a module import">;
def err_module_expected_semi : Error<"expected ';' after module name">;
def err_global_module_introducer_not_at_start
: Error<"'module;' introducing a global module fragment can appear only "
"at the start of the translation unit">;
def err_module_fragment_exported
: Error<"%select{global|private}0 module fragment cannot be exported">;
def err_private_module_fragment_expected_semi
: Error<"expected ';' after private module fragment declaration">;
def err_missing_before_module_end : Error<"expected %0 at end of module">;
def err_import_not_allowed_here
: Error<"imports must immediately follow the module declaration">;
def err_partition_import_outside_module
: Error<"module partition imports must be within a module purview">;
def err_import_in_wrong_fragment
: Error<"module%select{| partition}0 imports cannot be in the "
"%select{global|private}1 module fragment">;

def err_export_empty : Error<"export declaration cannot be empty">;
}

let CategoryName = "Generics Issue" in {
Expand Down
26 changes: 22 additions & 4 deletions clang/include/clang/Basic/IdentifierTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
LLVM_PREFERRED_TYPE(bool)
unsigned IsModulesImport : 1;

// True if this is the 'module' contextual keyword.
LLVM_PREFERRED_TYPE(bool)
unsigned IsModulesDecl : 1;

// True if this is a mangled OpenMP variant name.
LLVM_PREFERRED_TYPE(bool)
unsigned IsMangledOpenMPVariantName : 1;
Expand Down Expand Up @@ -215,8 +219,9 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
IsCPPOperatorKeyword(false), NeedsHandleIdentifier(false),
IsFromAST(false), ChangedAfterLoad(false), FEChangedAfterLoad(false),
RevertedTokenID(false), OutOfDate(false), IsModulesImport(false),
IsMangledOpenMPVariantName(false), IsDeprecatedMacro(false),
IsRestrictExpansion(false), IsFinal(false), IsKeywordInCpp(false) {}
IsModulesDecl(false), IsMangledOpenMPVariantName(false),
IsDeprecatedMacro(false), IsRestrictExpansion(false), IsFinal(false),
IsKeywordInCpp(false) {}

public:
IdentifierInfo(const IdentifierInfo &) = delete;
Expand Down Expand Up @@ -528,6 +533,18 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
RecomputeNeedsHandleIdentifier();
}

/// Determine whether this is the contextual keyword \c module.
bool isModulesDeclaration() const { return IsModulesDecl; }

/// Set whether this identifier is the contextual keyword \c module.
void setModulesDeclaration(bool I) {
IsModulesDecl = I;
if (I)
NeedsHandleIdentifier = true;
else
RecomputeNeedsHandleIdentifier();
}

/// Determine whether this is the mangled name of an OpenMP variant.
bool isMangledOpenMPVariantName() const { return IsMangledOpenMPVariantName; }

Expand Down Expand Up @@ -745,10 +762,11 @@ class IdentifierTable {
// contents.
II->Entry = &Entry;

// If this is the 'import' contextual keyword, mark it as such.
// If this is the 'import' or 'module' contextual keyword, mark it as such.
if (Name == "import")
II->setModulesImport(true);

else if (Name == "module")
II->setModulesDeclaration(true);
return *II;
}

Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ PPKEYWORD(pragma)
// C23 & C++26 #embed
PPKEYWORD(embed)

// C++20 Module Directive
PPKEYWORD(module)

// GNU Extensions.
PPKEYWORD(import)
PPKEYWORD(include_next)
Expand Down Expand Up @@ -1023,6 +1026,9 @@ ANNOTATION(module_include)
ANNOTATION(module_begin)
ANNOTATION(module_end)

// Annotations for C++, Clang and Objective-C named modules.
ANNOTATION(module_name)

// Annotation for a header_name token that has been looked up and transformed
// into the name of a header unit.
ANNOTATION(header_unit)
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Frontend/CompilerInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -863,7 +863,7 @@ class CompilerInstance : public ModuleLoader {
/// load it.
ModuleLoadResult findOrCompileModuleAndReadAST(StringRef ModuleName,
SourceLocation ImportLoc,
SourceLocation ModuleNameLoc,
SourceRange ModuleNameRange,
bool IsInclusionDirective);

/// Creates a \c CompilerInstance for compiling a module.
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Lex/CodeCompletionHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@
#ifndef LLVM_CLANG_LEX_CODECOMPLETIONHANDLER_H
#define LLVM_CLANG_LEX_CODECOMPLETIONHANDLER_H

#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/StringRef.h"

namespace clang {

class IdentifierInfo;
class MacroInfo;
using ModuleIdPath = ArrayRef<IdentifierLoc>;

/// Callback handler that receives notifications when performing code
/// completion within the preprocessor.
Expand Down Expand Up @@ -70,6 +73,11 @@ class CodeCompletionHandler {
/// file where we expect natural language, e.g., a comment, string, or
/// \#error directive.
virtual void CodeCompleteNaturalLanguage() { }

/// Callback invoked when performing code completion inside the module name
/// part of an import directive.
virtual void CodeCompleteModuleImport(SourceLocation ImportLoc,
ModuleIdPath Path) {}
};

}
Expand Down
10 changes: 5 additions & 5 deletions clang/include/clang/Lex/Lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class Lexer : public PreprocessorLexer {
//===--------------------------------------------------------------------===//
// Context that changes as the file is lexed.
// NOTE: any state that mutates when in raw mode must have save/restore code
// in Lexer::isNextPPTokenLParen.
// in Lexer::peekNextPPToken.

// BufferPtr - Current pointer into the buffer. This is the next character
// to be lexed.
Expand Down Expand Up @@ -645,10 +645,10 @@ class Lexer : public PreprocessorLexer {
BufferPtr = TokEnd;
}

/// isNextPPTokenLParen - Return 1 if the next unexpanded token will return a
/// tok::l_paren token, 0 if it is something else and 2 if there are no more
/// tokens in the buffer controlled by this lexer.
unsigned isNextPPTokenLParen();
/// peekNextPPToken - Return std::nullopt if there are no more tokens in the
/// buffer controlled by this lexer, otherwise return the next unexpanded
/// token.
std::optional<Token> peekNextPPToken();

//===--------------------------------------------------------------------===//
// Lexer character reading interfaces.
Expand Down
Loading
Loading