Skip to content

Commit

Permalink
✨ [Sema, Driver, Lex, Frontend] Implement naive #embed for C23 and C+…
Browse files Browse the repository at this point in the history
…+26.

🛠 [Frontend] Ensure commas inserted by #embed are properly serialized to output
  • Loading branch information
ThePhD authored and h-vetinari committed Oct 14, 2023
1 parent 08b20d8 commit 0b4b6a6
Show file tree
Hide file tree
Showing 46 changed files with 1,264 additions and 40 deletions.
3 changes: 2 additions & 1 deletion clang/CMakeLists.txt
Expand Up @@ -300,14 +300,15 @@ configure_file(
${CMAKE_CURRENT_BINARY_DIR}/include/clang/Basic/Version.inc)

# Add appropriate flags for GCC
option(CLANG_ENABLE_PEDANTIC "Compile with pedantic enabled." ON)
if (LLVM_COMPILER_IS_GCC_COMPATIBLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-common -Woverloaded-virtual")
if (NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing")
endif ()

# Enable -pedantic for Clang even if it's not enabled for LLVM.
if (NOT LLVM_ENABLE_PEDANTIC)
if (NOT LLVM_ENABLE_PEDANTIC AND CLANG_ENABLE_PEDANTIC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wno-long-long")
endif ()

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/Builtins.def
Expand Up @@ -1766,6 +1766,9 @@ BUILTIN(__builtin_ms_va_copy, "vc*&c*&", "n")
// Arithmetic Fence: to prevent FP reordering and reassociation optimizations
LANGBUILTIN(__arithmetic_fence, "v.", "tE", ALL_LANGUAGES)

// preprocessor embed builtin
LANGBUILTIN(__builtin_pp_embed, "v.", "tE", ALL_LANGUAGES)

#undef BUILTIN
#undef LIBBUILTIN
#undef LANGBUILTIN
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Expand Up @@ -708,6 +708,12 @@ def ReservedIdAsMacro : DiagGroup<"reserved-macro-identifier">;
def ReservedIdAsMacroAlias : DiagGroup<"reserved-id-macro", [ReservedIdAsMacro]>;
def RestrictExpansionMacro : DiagGroup<"restrict-expansion">;
def FinalMacro : DiagGroup<"final-macro">;
// Warnings about unknown preprocessor parameters (e.g. `#embed` and extensions)
def UnsupportedDirective : DiagGroup<"unsupported-directive">;
def UnknownDirectiveParameters : DiagGroup<"unknown-directive-parameters">;
def IgnoredDirectiveParameters : DiagGroup<"ignored-directive-parameters">;
def DirectiveParameters : DiagGroup<"directive-parameters",
[UnknownDirectiveParameters, IgnoredDirectiveParameters]>;

// Just silence warnings about -Wstrict-aliasing for now.
def : DiagGroup<"strict-aliasing=0">;
Expand Down
24 changes: 23 additions & 1 deletion clang/include/clang/Basic/DiagnosticLexKinds.td
Expand Up @@ -422,6 +422,22 @@ def warn_cxx23_compat_warning_directive : Warning<
def warn_c23_compat_warning_directive : Warning<
"#warning is incompatible with C standards before C23">,
InGroup<CPre23Compat>, DefaultIgnore;
def warn_c23_pp_embed : Warning<
"'__has_embed' is a C23 extension">,
InGroup<CPre23Compat>,
DefaultIgnore;
def warn_c23_pp_has_embed : Warning<
"'__has_embed' is a C23 extension">,
InGroup<CPre23Compat>,
DefaultIgnore;
def warn_cxx26_pp_embed : Warning<
"'__has_embed' is a C++26 extension">,
InGroup<CXXPre26Compat>,
DefaultIgnore;
def warn_cxx26_pp_has_embed : Warning<
"'__has_embed' is a C++26 extension">,
InGroup<CXXPre26Compat>,
DefaultIgnore;

def ext_pp_extra_tokens_at_eol : ExtWarn<
"extra tokens at end of #%0 directive">, InGroup<ExtraTokens>;
Expand Down Expand Up @@ -483,7 +499,13 @@ def ext_pp_gnu_line_directive : Extension<
def err_pp_invalid_directive : Error<
"invalid preprocessing directive%select{|, did you mean '#%1'?}0">;
def warn_pp_invalid_directive : Warning<
err_pp_invalid_directive.Summary>, InGroup<DiagGroup<"unknown-directives">>;
err_pp_invalid_directive.Summary>,
InGroup<UnsupportedDirective>;
def warn_pp_unknown_parameter_ignored : Warning<
"unknown%select{ | embed}0 preprocessor parameter '%1' ignored">,
InGroup<UnknownDirectiveParameters>;
def err_pp_unsupported_directive : Error<
"unsupported%select{ | embed}0 directive: %1">;
def err_pp_directive_required : Error<
"%0 must be used within a preprocessing directive">;
def err_pp_file_not_found : Error<"'%0' file not found">, DefaultFatal;
Expand Down
8 changes: 5 additions & 3 deletions clang/include/clang/Basic/FileManager.h
Expand Up @@ -276,11 +276,13 @@ class FileManager : public RefCountedBase<FileManager> {
/// MemoryBuffer if successful, otherwise returning null.
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBufferForFile(FileEntryRef Entry, bool isVolatile = false,
bool RequiresNullTerminator = true);
bool RequiresNullTerminator = true,
std::optional<int64_t> MaybeLimit = std::nullopt);
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBufferForFile(StringRef Filename, bool isVolatile = false,
bool RequiresNullTerminator = true) {
return getBufferForFileImpl(Filename, /*FileSize=*/-1, isVolatile,
bool RequiresNullTerminator = true,
std::optional<int64_t> MaybeLimit = std::nullopt) {
return getBufferForFileImpl(Filename, /*FileSize=*/(MaybeLimit ? *MaybeLimit : -1), isVolatile,
RequiresNullTerminator);
}

Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Expand Up @@ -126,6 +126,9 @@ PPKEYWORD(error)
// C99 6.10.6 - Pragma Directive.
PPKEYWORD(pragma)

// C23 & C++26 #embed
PPKEYWORD(embed)

// GNU Extensions.
PPKEYWORD(import)
PPKEYWORD(include_next)
Expand All @@ -151,6 +154,10 @@ TOK(eod) // End of preprocessing directive (end of line inside a
// directive).
TOK(code_completion) // Code completion marker

// #embed speed support
TOK(builtin_embed)


// C99 6.4.9: Comments.
TOK(comment) // Comment (only in -E -C[C] mode)

Expand Down
16 changes: 16 additions & 0 deletions clang/include/clang/Driver/Options.td
Expand Up @@ -114,6 +114,11 @@ def IncludePath_Group : OptionGroup<"<I/i group>">, Group<Preprocessor_Group>,
DocBrief<[{
Flags controlling how ``#include``\s are resolved to files.}]>;

def EmbedPath_Group : OptionGroup<"<Embed group>">, Group<Preprocessor_Group>,
DocName<"Embed path management">,
DocBrief<[{
Flags controlling how ``#embed``\s and similar are resolved to files.}]>;

def I_Group : OptionGroup<"<I group>">, Group<IncludePath_Group>, DocFlatten;
def i_Group : OptionGroup<"<i group>">, Group<IncludePath_Group>, DocFlatten;
def clang_i_Group : OptionGroup<"<clang i group>">, Group<i_Group>, DocFlatten;
Expand Down Expand Up @@ -816,6 +821,14 @@ will be ignored}]>;
def L : JoinedOrSeparate<["-"], "L">, Flags<[RenderJoined]>, Group<Link_Group>,
Visibility<[ClangOption, FlangOption]>,
MetaVarName<"<dir>">, HelpText<"Add directory to library search path">;
def embed_dir : JoinedOrSeparate<["-"], "embed-dir">,
Flags<[RenderJoined]>, Group<EmbedPath_Group>,
Visibility<[ClangOption, CC1Option, CC1AsOption, FlangOption, FC1Option]>,
MetaVarName<"<dir>">, HelpText<"Add directory to embed search path">;
def embed_dir_EQ : JoinedOrSeparate<["-"], "embed-dir=">,
Flags<[RenderJoined]>, Group<EmbedPath_Group>,
Visibility<[ClangOption, CC1Option, CC1AsOption, FlangOption, FC1Option]>,
MetaVarName<"<dir>">, HelpText<"Add directory to embed search path">;
def MD : Flag<["-"], "MD">, Group<M_Group>,
HelpText<"Write a depfile containing user and system headers">;
def MMD : Flag<["-"], "MMD">, Group<M_Group>,
Expand Down Expand Up @@ -1353,6 +1366,9 @@ def dD : Flag<["-"], "dD">, Group<d_Group>, Visibility<[ClangOption, CC1Option]>
def dI : Flag<["-"], "dI">, Group<d_Group>, Visibility<[ClangOption, CC1Option]>,
HelpText<"Print include directives in -E mode in addition to normal output">,
MarshallingInfoFlag<PreprocessorOutputOpts<"ShowIncludeDirectives">>;
def dE : Flag<["-"], "dE">, Group<d_Group>, Visibility<[ClangOption, CC1Option]>,
HelpText<"Print embed directives in -E mode in addition to normal output">,
MarshallingInfoFlag<PreprocessorOutputOpts<"ShowEmbedDirectives">>;
def dM : Flag<["-"], "dM">, Group<d_Group>, Visibility<[ClangOption, CC1Option]>,
HelpText<"Print macro definitions in -E mode instead of normal output">;
def dead__strip : Flag<["-"], "dead_strip">;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Frontend/PreprocessorOutputOptions.h
Expand Up @@ -22,6 +22,7 @@ class PreprocessorOutputOptions {
unsigned ShowMacroComments : 1; ///< Show comments, even in macros.
unsigned ShowMacros : 1; ///< Print macro definitions.
unsigned ShowIncludeDirectives : 1; ///< Print includes, imports etc. within preprocessed output.
unsigned ShowEmbedDirectives : 1; ///< Print embeds, etc. within preprocessed output.
unsigned RewriteIncludes : 1; ///< Preprocess include directives only.
unsigned RewriteImports : 1; ///< Include contents of transitively-imported modules.
unsigned MinimizeWhitespace : 1; ///< Ignore whitespace from input.
Expand All @@ -37,6 +38,7 @@ class PreprocessorOutputOptions {
ShowMacroComments = 0;
ShowMacros = 0;
ShowIncludeDirectives = 0;
ShowEmbedDirectives = 0;
RewriteIncludes = 0;
RewriteImports = 0;
MinimizeWhitespace = 0;
Expand Down
77 changes: 75 additions & 2 deletions clang/include/clang/Lex/PPCallbacks.h
Expand Up @@ -83,6 +83,47 @@ class PPCallbacks {
const Token &FilenameTok,
SrcMgr::CharacteristicKind FileType) {}

/// Callback invoked whenever the preprocessor cannot find a file for an
/// embed directive.
///
/// \param FileName The name of the file being included, as written in the
/// source code.
///
/// \returns true to indicate that the preprocessor should skip this file
/// and not issue any diagnostic.
virtual bool EmbedFileNotFound(StringRef FileName) { return false; }

/// Callback invoked whenever an embed directive has been processed,
/// regardless of whether the embed will actually find a file.
///
/// \param HashLoc The location of the '#' that starts the embed directive.
///
/// \param FileName The name of the file being included, as written in the
/// source code.
///
/// \param IsAngled Whether the file name was enclosed in angle brackets;
/// otherwise, it was enclosed in quotes.
///
/// \param FilenameRange The character range of the quotes or angle brackets
/// for the written file name.
///
/// \param ParametersRange The character range of the embed parameters. An
/// empty range if there were no parameters.
///
/// \param File The actual file that may be included by this embed directive.
///
/// \param SearchPath Contains the search path which was used to find the file
/// in the file system. If the file was found via an absolute path,
/// SearchPath will be empty.
///
/// \param RelativePath The path relative to SearchPath, at which the resource
/// file was found. This is equal to FileName.
virtual void EmbedDirective(SourceLocation HashLoc, StringRef FileName,
bool IsAngled, CharSourceRange FilenameRange,
CharSourceRange ParametersRange,
OptionalFileEntryRef File, StringRef SearchPath,
StringRef RelativePath) {}

/// Callback invoked whenever the preprocessor cannot find a file for an
/// inclusion directive.
///
Expand Down Expand Up @@ -330,11 +371,15 @@ class PPCallbacks {
SourceRange Range) {
}

/// Hook called when a '__has_embed' directive is read.
virtual void HasEmbed(SourceLocation Loc, StringRef FileName, bool IsAngled,
OptionalFileEntryRef File) {}

/// Hook called when a '__has_include' or '__has_include_next' directive is
/// read.
virtual void HasInclude(SourceLocation Loc, StringRef FileName, bool IsAngled,
OptionalFileEntryRef File,
SrcMgr::CharacteristicKind FileType);
SrcMgr::CharacteristicKind FileType) {}

/// Hook called when a source range is skipped.
/// \param Range The SourceRange that was skipped. The range begins at the
Expand Down Expand Up @@ -461,6 +506,25 @@ class PPChainedCallbacks : public PPCallbacks {
Second->FileSkipped(SkippedFile, FilenameTok, FileType);
}

bool EmbedFileNotFound(StringRef FileName) override {
bool Skip = First->FileNotFound(FileName);
// Make sure to invoke the second callback, no matter if the first already
// returned true to skip the file.
Skip |= Second->FileNotFound(FileName);
return Skip;
}

void EmbedDirective(SourceLocation HashLoc, StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange,
CharSourceRange ParametersRange,
OptionalFileEntryRef File, StringRef SearchPath,
StringRef RelativePath) override {
First->EmbedDirective(HashLoc, FileName, IsAngled, FilenameRange,
ParametersRange, File, SearchPath, RelativePath);
Second->EmbedDirective(HashLoc, FileName, IsAngled, FilenameRange,
ParametersRange, File, SearchPath, RelativePath);
}

bool FileNotFound(StringRef FileName) override {
bool Skip = First->FileNotFound(FileName);
// Make sure to invoke the second callback, no matter if the first already
Expand Down Expand Up @@ -561,9 +625,18 @@ class PPChainedCallbacks : public PPCallbacks {
Second->PragmaDiagnostic(Loc, Namespace, mapping, Str);
}

void HasEmbed(SourceLocation Loc, StringRef FileName, bool IsAngled,
OptionalFileEntryRef File) override {
First->HasEmbed(Loc, FileName, IsAngled, File);
Second->HasEmbed(Loc, FileName, IsAngled, File);
}

void HasInclude(SourceLocation Loc, StringRef FileName, bool IsAngled,
OptionalFileEntryRef File,
SrcMgr::CharacteristicKind FileType) override;
SrcMgr::CharacteristicKind FileType) override {
First->HasInclude(Loc, FileName, IsAngled, File, FileType);
Second->HasInclude(Loc, FileName, IsAngled, File, FileType);
}

void PragmaOpenCLExtension(SourceLocation NameLoc, const IdentifierInfo *Name,
SourceLocation StateLoc, unsigned State) override {
Expand Down

0 comments on commit 0b4b6a6

Please sign in to comment.