aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Lex/PPMacroExpansion.cpp
diff options
context:
space:
mode:
authorThe Phantom Derpstorm <phdofthehouse@gmail.com>2024-06-12 03:16:02 -0400
committerGitHub <noreply@github.com>2024-06-12 09:16:02 +0200
commit5989450e0061dce8cff89d8acfdd5225c14cd065 (patch)
treef27a31bdd90497b029718f283d0702fec58e8477 /clang/lib/Lex/PPMacroExpansion.cpp
parentb83f8c75e4cccf25abbe4ad76406ba0c382bf336 (diff)
downloadllvm-5989450e0061dce8cff89d8acfdd5225c14cd065.zip
llvm-5989450e0061dce8cff89d8acfdd5225c14cd065.tar.gz
llvm-5989450e0061dce8cff89d8acfdd5225c14cd065.tar.bz2
[clang][Sema, Lex, Parse] Preprocessor embed in C and C++ (and Obj-C and Obj-C++ by-proxy) (#68620)
This commit implements the entirety of the now-accepted [N3017 - Preprocessor Embed](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3017.htm) and its sister C++ paper [p1967](https://wg21.link/p1967). It implements everything in the specification, and includes an implementation that drastically improves the time it takes to embed data in specific scenarios (the initialization of character type arrays). The mechanisms used to do this are used under the "as-if" rule, and in general when the system cannot detect it is initializing an array object in a variable declaration, will generate EmbedExpr AST node which will be expanded by AST consumers (CodeGen or constant expression evaluators) or expand embed directive as a comma expression. --------- Co-authored-by: Aaron Ballman <aaron@aaronballman.com> Co-authored-by: cor3ntin <corentinjabot@gmail.com> Co-authored-by: H. Vetinari <h.vetinari@gmx.com> Co-authored-by: Podchishchaeva, Mariya <mariya.podchishchaeva@intel.com>
Diffstat (limited to 'clang/lib/Lex/PPMacroExpansion.cpp')
-rw-r--r--clang/lib/Lex/PPMacroExpansion.cpp111
1 files changed, 111 insertions, 0 deletions
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index f085b94..3913ff0 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -380,6 +380,7 @@ void Preprocessor::RegisterBuiltinMacros() {
Ident__has_c_attribute = nullptr;
Ident__has_declspec = RegisterBuiltinMacro(*this, "__has_declspec_attribute");
+ Ident__has_embed = RegisterBuiltinMacro(*this, "__has_embed");
Ident__has_include = RegisterBuiltinMacro(*this, "__has_include");
Ident__has_include_next = RegisterBuiltinMacro(*this, "__has_include_next");
Ident__has_warning = RegisterBuiltinMacro(*this, "__has_warning");
@@ -1279,6 +1280,105 @@ static bool EvaluateHasIncludeCommon(Token &Tok, IdentifierInfo *II,
return File.has_value();
}
+/// EvaluateHasEmbed - Process a '__has_embed("foo" params...)' expression.
+/// Returns a filled optional with the value if successful; otherwise, empty.
+EmbedResult Preprocessor::EvaluateHasEmbed(Token &Tok, IdentifierInfo *II) {
+ // These expressions are only allowed within a preprocessor directive.
+ if (!this->isParsingIfOrElifDirective()) {
+ Diag(Tok, diag::err_pp_directive_required) << II;
+ // Return a valid identifier token.
+ assert(Tok.is(tok::identifier));
+ Tok.setIdentifierInfo(II);
+ return EmbedResult::Invalid;
+ }
+
+ // Ensure we have a '('.
+ LexUnexpandedToken(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ Diag(Tok, diag::err_pp_expected_after) << II << tok::l_paren;
+ // If the next token looks like a filename or the start of one,
+ // assume it is and process it as such.
+ return EmbedResult::Invalid;
+ }
+
+ // Save '(' location for possible missing ')' message and then lex the header
+ // name token for the embed resource.
+ SourceLocation LParenLoc = Tok.getLocation();
+ if (this->LexHeaderName(Tok))
+ return EmbedResult::Invalid;
+
+ if (Tok.isNot(tok::header_name)) {
+ Diag(Tok.getLocation(), diag::err_pp_expects_filename);
+ return EmbedResult::Invalid;
+ }
+
+ SourceLocation FilenameLoc = Tok.getLocation();
+ Token FilenameTok = Tok;
+
+ std::optional<LexEmbedParametersResult> Params =
+ this->LexEmbedParameters(Tok, /*ForHasEmbed=*/true);
+ assert((Params || Tok.is(tok::eod)) &&
+ "expected success or to be at the end of the directive");
+
+ if (!Params)
+ return EmbedResult::Invalid;
+
+ if (Params->UnrecognizedParams > 0)
+ return EmbedResult::NotFound;
+
+ if (!Tok.is(tok::r_paren)) {
+ Diag(this->getLocForEndOfToken(FilenameLoc), diag::err_pp_expected_after)
+ << II << tok::r_paren;
+ Diag(LParenLoc, diag::note_matching) << tok::l_paren;
+ if (Tok.isNot(tok::eod))
+ DiscardUntilEndOfDirective();
+ return EmbedResult::Invalid;
+ }
+
+ SmallString<128> FilenameBuffer;
+ StringRef Filename = this->getSpelling(FilenameTok, FilenameBuffer);
+ bool isAngled =
+ this->GetIncludeFilenameSpelling(FilenameTok.getLocation(), Filename);
+ // If GetIncludeFilenameSpelling set the start ptr to null, there was an
+ // error.
+ assert(!Filename.empty());
+ const FileEntry *LookupFromFile =
+ this->getCurrentFileLexer() ? *this->getCurrentFileLexer()->getFileEntry()
+ : static_cast<FileEntry *>(nullptr);
+ OptionalFileEntryRef MaybeFileEntry =
+ this->LookupEmbedFile(Filename, isAngled, false, LookupFromFile);
+ if (Callbacks) {
+ Callbacks->HasEmbed(LParenLoc, Filename, isAngled, MaybeFileEntry);
+ }
+ if (!MaybeFileEntry)
+ return EmbedResult::NotFound;
+
+ size_t FileSize = MaybeFileEntry->getSize();
+ // First, "offset" into the file (this reduces the amount of data we can read
+ // from the file).
+ if (Params->MaybeOffsetParam) {
+ if (Params->MaybeOffsetParam->Offset > FileSize)
+ FileSize = 0;
+ else
+ FileSize -= Params->MaybeOffsetParam->Offset;
+ }
+
+ // Second, limit the data from the file (this also reduces the amount of data
+ // we can read from the file).
+ if (Params->MaybeLimitParam) {
+ if (Params->MaybeLimitParam->Limit > FileSize)
+ FileSize = 0;
+ else
+ FileSize = Params->MaybeLimitParam->Limit;
+ }
+
+ // If we have no data left to read, the file is empty, otherwise we have the
+ // expected resource.
+ if (FileSize == 0)
+ return EmbedResult::Empty;
+ return EmbedResult::Found;
+}
+
bool Preprocessor::EvaluateHasInclude(Token &Tok, IdentifierInfo *II) {
return EvaluateHasIncludeCommon(Tok, II, *this, nullptr, nullptr);
}
@@ -1820,6 +1920,17 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
return;
OS << (int)Value;
Tok.setKind(tok::numeric_constant);
+ } else if (II == Ident__has_embed) {
+ // The argument to these two builtins should be a parenthesized
+ // file name string literal using angle brackets (<>) or
+ // double-quotes (""), optionally followed by a series of
+ // arguments similar to form like attributes.
+ EmbedResult Value = EvaluateHasEmbed(Tok, II);
+ if (Value == EmbedResult::Invalid)
+ return;
+
+ Tok.setKind(tok::numeric_constant);
+ OS << static_cast<int>(Value);
} else if (II == Ident__has_warning) {
// The argument should be a parenthesized string literal.
EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,