diff options
author | The Phantom Derpstorm <phdofthehouse@gmail.com> | 2024-06-12 03:16:02 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-12 09:16:02 +0200 |
commit | 5989450e0061dce8cff89d8acfdd5225c14cd065 (patch) | |
tree | f27a31bdd90497b029718f283d0702fec58e8477 /clang/lib/Lex/PPMacroExpansion.cpp | |
parent | b83f8c75e4cccf25abbe4ad76406ba0c382bf336 (diff) | |
download | llvm-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.cpp | 111 |
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, |