https://github.com/ThePhD created
https://github.com/llvm/llvm-project/pull/190578
This is a draft pull request containing a mostly-reasonable implementation of
`#depend` and the builtin `__builtin_std_embed`. It's a draft because the
proposal has not yet fully passed WG21 and is bouncing back and forth due to
Committee-internal shenanigans, but this will prevent me from having to
re-re-reimplement and re-re-rejustify what it would take to make a `consteval`
builtin embed work.
This pull request will remain a draft until the following is completed:
- [ ] p1040 passes WG21
- [ ] Additional implementation to convert UTF-8/16/32 `char8_t`/`wchar_t` to
EBCDIC rather than UTF-8 internally (use routines in `llvm/Support` and
`clang/Basic`)
- [ ] Additional implementations of `std::embed` are added for a fixed
`N`/`_Limit` template parameter (which require an exact count)
- [ ] `__builtin_std_embed` with bad arguments
- [ ] Documentation is added for the `__builtin_std_embed`
- [ ] An entry is added for the c++ status page
(clang/www/cxx_status.html)
- [ ] An entry is added in the builtins documentation
(clang/docs/LanguageExtensions.rst)
- [ ] Make sure to mention (lack of) support for quirky files such as
block devices, etc.
- [ ] Tests are added for `__builtin_std_embed`
- [ ] error reporting with bad arguments
- [ ] error reporting for too-large numbers on AMD64/ARM64 systems
(internally limit is `int64_t`, not `uint64_t`, but takes `std::size_t`)
- [ ] error reporting for bad conversions to UTF-8 internals from
UTF-16/UTF-32 wide-character strings on appropriate hardware (TODO: figure out
how "improper" characters that may undergo conversion are handled -- likely
hard error since replacement with `?` or `U+FFFD` would just result in failures
(`?` is not allowed) or bad file searches (for `U+FFFD` as UTF-8))
- [ ] error reporting for missing `#depend`
- [ ] testing structures with implicit conversions to appropriate
pointer types
- [ ] error reporting for structures with implicit conversions to
inappropriate pointer types
- [ ] error reporting for appropriate `#depend` but file not found
- [ ] Tests are added for `std::embed`
- [ ] one billion `static_assert`s, use of `__FILE__`
>From be8d392a5cfb733b98960e08e522e5d1438a84c4 Mon Sep 17 00:00:00 2001
From: ThePhD <[email protected]>
Date: Wed, 1 Apr 2026 19:06:16 +0200
Subject: [PATCH] =?UTF-8?q?[Clang][Lex][Sema]=20=E2=9C=A8=20Full=20std::em?=
=?UTF-8?q?bed=20and=20#depend=20implementation?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
clang/include/clang/AST/ASTContext.h | 15 ++
clang/include/clang/Basic/Builtins.td | 7 +
clang/include/clang/Basic/DiagnosticGroups.td | 2 +-
.../include/clang/Basic/DiagnosticLexKinds.td | 4 +
.../clang/Basic/InputDependencyCollection.h | 66 +++++
clang/include/clang/Basic/ResourceSearch.h | 37 +++
clang/include/clang/Basic/TokenKinds.def | 2 +
clang/include/clang/Lex/PPCallbacks.h | 16 ++
clang/include/clang/Lex/Preprocessor.h | 9 +-
clang/include/clang/Sema/Sema.h | 1 +
clang/lib/AST/ASTContext.cpp | 12 +
clang/lib/AST/ExprConstant.cpp | 234 +++++++++++++++++-
clang/lib/Basic/CMakeLists.txt | 2 +
clang/lib/Basic/IdentifierTable.cpp | 1 +
clang/lib/Basic/InputDependencyCollection.cpp | 181 ++++++++++++++
clang/lib/Basic/ResourceSearch.cpp | 107 ++++++++
clang/lib/Frontend/ASTUnit.cpp | 8 +-
clang/lib/Frontend/CompilerInstance.cpp | 2 +
clang/lib/Frontend/DependencyFile.cpp | 91 ++++++-
clang/lib/Lex/PPDirectives.cpp | 128 +++++-----
clang/lib/Lex/Preprocessor.cpp | 22 +-
clang/lib/Sema/SemaChecking.cpp | 151 +++++++++++
.../clang-import-test/clang-import-test.cpp | 2 +
clang/unittests/Lex/PPCallbacksTest.cpp | 1 +
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/embed | 113 +++++++++
libcxx/include/version | 7 +
27 files changed, 1135 insertions(+), 87 deletions(-)
create mode 100644 clang/include/clang/Basic/InputDependencyCollection.h
create mode 100644 clang/include/clang/Basic/ResourceSearch.h
create mode 100644 clang/lib/Basic/InputDependencyCollection.cpp
create mode 100644 clang/lib/Basic/ResourceSearch.cpp
create mode 100644 libcxx/include/embed
diff --git a/clang/include/clang/AST/ASTContext.h
b/clang/include/clang/AST/ASTContext.h
index ba1b58489c327..56cf139cad791 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -28,6 +28,7 @@
#include "clang/AST/Type.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/LLVM.h"
+#include "clang/Basic/InputDependencyCollection.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/DenseMap.h"
@@ -131,6 +132,7 @@ class OMPTraitInfo;
class ParentMapContext;
struct ParsedTargetAttr;
class Preprocessor;
+class PreprocessorOptions;
class ProfileList;
class StoredDeclsMap;
class TargetAttr;
@@ -567,6 +569,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
// A mapping from Scalable Vector Type keys to their corresponding QualType.
mutable llvm::DenseMap<llvm::ScalableVecTyKey, QualType> ScalableVecTyMap;
+ // Same preprocessor options from other lexers, so we can look up
+ // embedded files
+ PreprocessorOptions const *PPOpts = nullptr;
+
+
ASTContext &this_() { return *this; }
public:
@@ -802,6 +809,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable DeclarationNameTable DeclarationNames;
IntrusiveRefCntPtr<ExternalASTSource> ExternalSource;
ASTMutationListener *Listener = nullptr;
+ std::shared_ptr<InputDependencyCollection> InputDependencyPatterns =
std::make_shared<InputDependencyCollection>();
/// Returns the clang bytecode interpreter context.
interp::Context &getInterpContext() const;
@@ -1396,6 +1404,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
ASTContext &operator=(const ASTContext &) = delete;
~ASTContext();
+ /// Set the preprocessor options to use right now
+ void setCurrentPreprocessorOptions(PreprocessorOptions const *NewPPOpts);
+ void setCurrentPreprocessorOptions(const PreprocessorOptions &NewPPOpts);
+
+ /// Get the preprocessor options we are using. Can be null!
+ PreprocessorOptions const* getCurrentPreprocessorOptions() const;
+
/// Attach an external AST source to the AST context.
///
/// The external AST source provides the ability to load parts of
diff --git a/clang/include/clang/Basic/Builtins.td
b/clang/include/clang/Basic/Builtins.td
index b8bbc544595e2..d33dc0fe32233 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1006,6 +1006,13 @@ def GetVtablePointer : LangBuiltin<"CXX_LANG"> {
let Prototype = "void*(void*)";
}
+// p1040 std::embed
+def StdEmbed : Builtin {
+ let Spellings = ["__builtin_std_embed"];
+ let Attributes = [NoThrow, Const, Consteval];
+ let Prototype = "void const*(size_t&, ...)";
+}
+
// GCC exception builtins
def EHReturn : Builtin {
let Spellings = ["__builtin_eh_return"];
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td
b/clang/include/clang/Basic/DiagnosticGroups.td
index dc7280c66ea79..de7e782ef6174 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1520,7 +1520,7 @@ def : DiagGroup<"c++1y-extensions", [CXX14]>;
def : DiagGroup<"c++1z-extensions", [CXX17]>;
def : DiagGroup<"c++2a-extensions", [CXX20]>;
def : DiagGroup<"c++2b-extensions", [CXX23]>;
-def : DiagGroup<"c++2c-extensions", [CXX26]>;
+def CXX2C : DiagGroup<"c++2c-extensions", [CXX26]>;
def DelegatingCtorCycles :
DiagGroup<"delegating-ctor-cycles">;
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td
b/clang/include/clang/Basic/DiagnosticLexKinds.td
index bea0aafac98cf..433379d680857 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -464,6 +464,9 @@ def warn_c23_compat_warning_directive : Warning<
def ext_pp_embed_directive : ExtWarn<
"#embed is a %select{C23|Clang}0 extension">,
InGroup<C23>;
+def ext_pp_depend_directive : ExtWarn<
+ "#depend is a %select{C++2c|Clang}0 extension">,
+ InGroup<CXX2C>, DefaultIgnore;
def warn_compat_pp_embed_directive : Warning<
"#embed is incompatible with C standards before C23">,
InGroup<CPre23Compat>, DefaultIgnore;
@@ -579,6 +582,7 @@ def err_pp_including_mainfile_in_preamble : Error<
def err_pp_empty_filename : Error<"empty filename">;
def err_pp_include_too_deep : Error<"#include nested too deeply">;
def err_pp_expects_filename : Error<"expected \"FILENAME\" or <FILENAME>">;
+def err_pp_expects_pattern : Error<"expected \"PATTERN\" or <PATTERN>">;
def err_pp_macro_not_identifier : Error<"macro name must be an identifier">;
def err_pp_missing_macro_name : Error<"macro name missing">;
def err_pp_missing_rparen_in_macro_def : Error<
diff --git a/clang/include/clang/Basic/InputDependencyCollection.h
b/clang/include/clang/Basic/InputDependencyCollection.h
new file mode 100644
index 0000000000000..d29a9e4192f37
--- /dev/null
+++ b/clang/include/clang/Basic/InputDependencyCollection.h
@@ -0,0 +1,66 @@
+//===--- InputDependencyCollection.h - Searching for Resources --*- C++
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// User-provided filters include/exclude profile instrumentation in certain
+// functions.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_BASIC_INPUTDEPENDENCYCOLLECTION_H
+#define LLVM_CLANG_BASIC_INPUTDEPENDENCYCOLLECTION_H
+
+#include "clang/Basic/FileEntry.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/Regex.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+
+class FileManager;
+
+/// How to handle various forms of input dependency root patterns for search
+/// purposes.
+enum class RootPatternScanType {
+ None = 0b00,
+ Directory = 0b01,
+ RecursiveDirectory = 0b10,
+ DirectoryAndRecursiveDirectory = 0b11
+};
+
+struct PatternFilter {
+ std::string Input;
+ std::string SearchRoot;
+ std::string PatternRoot;
+ std::string Pattern;
+ llvm::Regex PatternRegex;
+ RootPatternScanType RootHandling;
+ bool Exported;
+
+ PatternFilter(std::string Input);
+
+ bool Check(StringRef Filename) const;
+};
+
+class InputDependencyCollection {
+private:
+ std::vector<PatternFilter> PatternFilters;
+
+ static PatternFilter ComputeFilter(std::string Pattern, bool Exported);
+
+public:
+ InputDependencyCollection() = default;
+
+ PatternFilter &Add(std::string Pattern, bool IsAngled, bool Exported,
+ FileManager &FM,
+ const std::vector<std::string> &SearchEntries,
+ OptionalFileEntryRef LookupFrom);
+ bool Check(llvm::StringRef Filename) const;
+};
+} // namespace clang
+
+#endif
diff --git a/clang/include/clang/Basic/ResourceSearch.h
b/clang/include/clang/Basic/ResourceSearch.h
new file mode 100644
index 0000000000000..da163b9698841
--- /dev/null
+++ b/clang/include/clang/Basic/ResourceSearch.h
@@ -0,0 +1,37 @@
+//===--- ResourceSearch.h - Searching for Resources -------------*- C++
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// User-provided filters include/exclude profile instrumentation in certain
+// functions.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_BASIC_RESOURCESEARCH_H
+#define LLVM_CLANG_BASIC_RESOURCESEARCH_H
+
+#include "clang/Basic/FileEntry.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include <memory>
+#include <optional>
+
+namespace clang {
+
+class FileManager;
+
+OptionalFileEntryRef
+LookupFileWithStdVec(StringRef Filename, bool isAngled, bool OpenFile,
+ FileManager &FM,
+ const std::vector<std::string> &SearchEntries,
+ OptionalFileEntryRef LookupFromFile);
+OptionalFileEntryRef LookupFileWith(StringRef Filename, bool isAngled,
+ bool OpenFile, FileManager &FM,
+ ArrayRef<StringRef> SearchEntries,
+ OptionalFileEntryRef LookupFromFile);
+} // namespace clang
+
+#endif
diff --git a/clang/include/clang/Basic/TokenKinds.def
b/clang/include/clang/Basic/TokenKinds.def
index 8b9f613037718..28f706ce87f70 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -132,6 +132,8 @@ PPKEYWORD(pragma)
// C23 & C++26 #embed
PPKEYWORD(embed)
+// C++29 #depend
+PPKEYWORD(depend)
// C++20 Module Directive
PPKEYWORD(module)
diff --git a/clang/include/clang/Lex/PPCallbacks.h
b/clang/include/clang/Lex/PPCallbacks.h
index 51c6a31e143b8..fbe31e7cf649b 100644
--- a/clang/include/clang/Lex/PPCallbacks.h
+++ b/clang/include/clang/Lex/PPCallbacks.h
@@ -29,6 +29,7 @@ class MacroDefinition;
class MacroDirective;
class MacroArgs;
struct LexEmbedParametersResult;
+struct PatternFilter;
/// This interface provides a way to observe the actions of the
/// preprocessor as it does its thing.
@@ -175,6 +176,12 @@ class PPCallbacks {
bool ModuleImported,
SrcMgr::CharacteristicKind FileType) {}
+ /// Hook called when a 'depend' directive is read.
+ virtual void DependDirective(SourceLocation HashLoc, const Token &DependTok,
+ StringRef FileName, bool IsAngled,
+ const PatternFilter& Filter,
+ OptionalFileEntryRef CurrentFile) {}
+
/// Callback invoked whenever a submodule was entered.
///
/// \param M The submodule we have entered.
@@ -543,6 +550,15 @@ class PPChainedCallbacks : public PPCallbacks {
SuggestedModule, ModuleImported, FileType);
}
+ /// Hook called whenever an \#depend is seen.
+ void DependDirective(SourceLocation HashLoc, const Token &DependTok,
+ StringRef Pattern, bool IsAngled,
+ const PatternFilter& Filter,
+ OptionalFileEntryRef CurrentFile) override {
+ First->DependDirective(HashLoc, DependTok, Pattern, IsAngled, Filter,
CurrentFile);
+ Second->DependDirective(HashLoc, DependTok, Pattern, IsAngled, Filter,
CurrentFile);
+ }
+
void EnteredSubmodule(Module *M, SourceLocation ImportLoc,
bool ForPragma) override {
First->EnteredSubmodule(M, ImportLoc, ForPragma);
diff --git a/clang/include/clang/Lex/Preprocessor.h
b/clang/include/clang/Lex/Preprocessor.h
index c7e152a75f51f..33e0984cd7d80 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -17,6 +17,7 @@
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/InputDependencyCollection.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Module.h"
@@ -343,6 +344,9 @@ class Preprocessor {
/// The kind of translation unit we are processing.
const TranslationUnitKind TUKind;
+ /// The #depend dependency patterns seen in this translation unit
+ std::shared_ptr<InputDependencyCollection> InputDependencyPatterns =
std::make_shared<InputDependencyCollection>();
+
/// Returns a pointer into the given file's buffer that's guaranteed
/// to be between tokens. The returned pointer is always before \p Start.
/// The maximum distance betweenthe returned pointer and \p Start is
@@ -1812,7 +1816,8 @@ class Preprocessor {
void LexTokensUntilEOF(std::vector<Token> *Tokens = nullptr);
/// Lex a token, forming a header-name token if possible.
- bool LexHeaderName(Token &Result, bool AllowMacroExpansion = true);
+ bool LexHeaderName(Token &Result, bool AllowMacroExpansion = true,
+ bool SkipFirst = false);
/// Lex the parameters for an #embed directive, returns nullopt on error.
std::optional<LexEmbedParametersResult> LexEmbedParameters(Token &Current,
@@ -2895,6 +2900,8 @@ class Preprocessor {
const FileEntry *LookupFromFile, StringRef &LookupFilename,
SmallVectorImpl<char> &RelativePath, SmallVectorImpl<char> &SearchPath,
ModuleMap::KnownHeader &SuggestedModule, bool isAngled);
+ // Input dependency caching
+ void HandleDependDirective(SourceLocation HashLoc, Token &Tok);
// Binary data inclusion
void HandleEmbedDirective(SourceLocation HashLoc, Token &Tok);
void HandleEmbedDirectiveImpl(SourceLocation HashLoc,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1b8a9803be472..a4ad6b01b52ae 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3053,6 +3053,7 @@ class Sema final : public SemaBase {
bool CheckInvalidBuiltinCountedByRef(const Expr *E,
BuiltinCountedByRefKind K);
bool BuiltinCountedByRef(CallExpr *TheCall);
+ bool BuiltinStdEmbed(CallExpr *TheCall);
// Matrix builtin handling.
ExprResult BuiltinMatrixTranspose(CallExpr *TheCall, ExprResult CallResult);
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index ee7f823b014b2..d0cde5397663b 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -903,6 +903,18 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager
&SM,
addTranslationUnitDecl();
}
+void ASTContext::setCurrentPreprocessorOptions(const PreprocessorOptions
&NewOpts) {
+ PPOpts = &NewOpts;
+}
+
+void ASTContext::setCurrentPreprocessorOptions(PreprocessorOptions const
*NewOpts) {
+ PPOpts = NewOpts;
+}
+
+PreprocessorOptions const *ASTContext::getCurrentPreprocessorOptions() const {
+ return PPOpts;
+}
+
void ASTContext::cleanup() {
// Release the DenseMaps associated with DeclContext objects.
// FIXME: Is this the ideal solution?
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 4f45fa728c605..084fe16bf04f2 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -53,13 +53,18 @@
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticSema.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/ResourceSearch.h"
+#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetBuiltins.h"
#include "clang/Basic/TargetInfo.h"
+#include "clang/Lex/PreprocessorOptions.h"
#include "llvm/ADT/APFixedPoint.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Casting.h"
+#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/SipHash.h"
@@ -3544,13 +3549,16 @@ static APSInt extractStringLiteralCharacter(EvalInfo
&Info, const Expr *Lit,
if (auto PE = dyn_cast<PredefinedExpr>(Lit))
Lit = PE->getFunctionName();
const StringLiteral *S = cast<StringLiteral>(Lit);
+ const StringLiteralKind SLK = S->getKind();
const ConstantArrayType *CAT =
Info.Ctx.getAsConstantArrayType(S->getType());
assert(CAT && "string literal isn't an array");
QualType CharType = CAT->getElementType();
- assert(CharType->isIntegerType() && "unexpected character type");
+ assert(((SLK == StringLiteralKind::Binary &&
CharType->isIntegralOrEnumerationType())
+ || (SLK != StringLiteralKind::Binary && CharType->isIntegerType()))
+ && "unexpected character type");
APSInt Value(Info.Ctx.getTypeSize(CharType),
- CharType->isUnsignedIntegerType());
+ CharType->isUnsignedIntegerOrEnumerationType());
if (Index < S->getLength())
Value = S->getCodeUnit(Index);
return Value;
@@ -10688,7 +10696,223 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const
CallExpr *E,
return false;
}
}
+ case Builtin::BI__builtin_std_embed: {
+ const Expr *SizeOutArg = E->getArg(0);
+ const Expr *PtrSpecifierArg = E->getArg(1);
+ const Expr *ResourceNameSizeArg = E->getArg(2);
+ const Expr *ResourceNamePtrArg = E->getArg(3);
+ const Expr *OffsetArg = E->getArg(4);
+ const Expr *LimitArg = E->getNumArgs() == 6 ? E->getArg(5) : nullptr;
+
+ QualType ArrElementTy = PtrSpecifierArg->getType()->getPointeeType();
+
+ LValue ResourceNamePtrLVal;
+ if (!evaluatePointer(ResourceNamePtrArg, ResourceNamePtrLVal)) {
+ return Error(ResourceNamePtrArg);
+ }
+
+ APSInt ResourceNameSizeVal;
+ if (!EvaluateInteger(ResourceNameSizeArg, ResourceNameSizeVal, Info)) {
+ return Error(ResourceNameSizeArg);
+ }
+ if (ResourceNameSizeVal.getBitWidth() > 64) {
+ Info.FFDiag(ResourceNameSizeArg->getBeginLoc(), diag::err_ice_too_large)
<< OffsetArg << 64 << 1;
+ return false;
+ }
+ APValue ResourceNamePtrVal;
+ ResourceNamePtrLVal.moveInto(ResourceNamePtrVal);
+ uint64_t ResourceNameSize = ResourceNameSizeVal.getZExtValue();
+
+ std::string ResourceName;
+ const QualType SizeTy = Info.Ctx.getSizeType();
+ const QualType WCharTy = Info.Ctx.getWideCharType();
+ const size_t SizeTySize = Info.Ctx.getTypeSize(SizeTy);
+ const size_t WCharTySize = Info.Ctx.getTypeSize(WCharTy);
+ const QualType
ResourceNameCharTy(ResourceNamePtrArg->getType()->getPointeeOrArrayElementType(),
0);
+ if (ResourceNameCharTy->isChar8Type() || ResourceNameCharTy->isCharType()
|| (ResourceNameCharTy->isWideCharType() && WCharTySize == 8)) {
+ // Assume the ResourceName is directly usable as an 8-bit transmuation
+ for (size_t Index = 0; Index < ResourceNameSize; ++Index) {
+ APValue Char;
+ if (!handleLValueToRValueConversion(Info, ResourceNamePtrArg,
+ ResourceNameCharTy, ResourceNamePtrLVal, Char)) {
+ return Error(ResourceNamePtrArg);
+ }
+ ResourceName.push_back(static_cast<char>(static_cast<unsigned
char>(Char.getInt().getExtValue())));
+
+ if (!HandleLValueArrayAdjustment(Info, ResourceNamePtrArg,
+ ResourceNamePtrLVal, ResourceNameCharTy, 1)) {
+ return Error(ResourceNamePtrArg);
+ }
+ }
+ }
+ else if (ResourceNameCharTy->isWideCharType()) {
+ // we assume either UTF-16, or UTF-32 based on the size of the string
+ // transmute accordingly
+ if (WCharTySize == 16) {
+ llvm::SmallVector<llvm::UTF16, 64> ResourceNameU16;
+ for (size_t Index = 0; Index < ResourceNameSize; ++Index) {
+ APValue Char;
+ if (!handleLValueToRValueConversion(Info, ResourceNamePtrArg,
+ ResourceNameCharTy, ResourceNamePtrLVal, Char)) {
+ return Error(ResourceNamePtrArg);
+ }
+
ResourceName.push_back(static_cast<llvm::UTF16>(Char.getInt().getExtValue()));
+
+ if (!HandleLValueArrayAdjustment(Info, ResourceNamePtrArg,
+ ResourceNamePtrLVal, ResourceNameCharTy, 1)) {
+ return Error(ResourceNamePtrArg);
+ }
+ }
+ if (!llvm::convertUTF16ToUTF8String(ResourceNameU16, ResourceName)){
+ // error: bad name conversion
+ return Error(ResourceNamePtrArg);
+ }
+ }
+ else if (WCharTySize == 32) {
+ llvm::SmallVector<llvm::UTF32, 64> ResourceNameU32;
+ for (size_t Index = 0; Index < ResourceNameSize; ++Index) {
+ APValue Char;
+ if (!handleLValueToRValueConversion(Info, ResourceNamePtrArg,
+ ResourceNameCharTy, ResourceNamePtrLVal, Char)) {
+ return false;
+ }
+
ResourceName.push_back(static_cast<llvm::UTF32>(Char.getInt().getExtValue()));
+
+ if (!HandleLValueArrayAdjustment(Info, ResourceNamePtrArg,
+ ResourceNamePtrLVal, ResourceNameCharTy, 1)) {
+ return false;
+ }
+ }
+ if (!llvm::convertUTF32ToUTF8String(ResourceNameU32, ResourceName)) {
+ // error: bad name conversion
+ return Error(ResourceNamePtrArg);
+ }
+ }
+ else {
+ llvm::report_fatal_error("The filename has a wide character type that
cannot be converted to a UTF-8/multibyte string");
+ }
+ }
+ else {
+ llvm::report_fatal_error("The filename has an unusuable or
unrecognizable character type");
+ }
+
+ uint64_t DataSize = 0;
+ uint64_t DataOffset = 0;
+ std::optional<int64_t> MaybeLimit = std::nullopt;
+
+ APSInt OffsetVal;
+ if (!EvaluateInteger(OffsetArg, OffsetVal, Info)) {
+ return Error(OffsetArg);
+ }
+ if (OffsetVal.getBitWidth() > 64) {
+ Info.FFDiag(OffsetArg->getBeginLoc(), diag::err_ice_too_large) <<
OffsetArg << 64 << 0;
+ return false;
+ }
+ DataOffset = OffsetVal.getZExtValue();
+ if (LimitArg) {
+ APSInt LimitVal;
+ if (!EvaluateInteger(LimitArg, LimitVal, Info)) {
+ return Error(OffsetArg);
+ }
+ if (LimitVal.getBitWidth() > 64) {
+ Info.FFDiag(LimitArg->getBeginLoc(), diag::err_ice_too_large) <<
LimitArg << 64 << 0;
+ return false;
+ }
+ uint64_t FullLimit = LimitVal.getZExtValue();
+ if (FullLimit >
static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
+ // error: implementation limit here since we can only
+ // have a 63-bit unsigned number, not a 64-bit one
+ Info.FFDiag(LimitArg->getBeginLoc(), diag::err_ice_too_large) <<
FullLimit << 64 << 0;
+ return false;
+ }
+ MaybeLimit = static_cast<int64_t>(FullLimit);
+ }
+
+ SourceManager &SM = Info.Ctx.getSourceManager();
+ FileManager &FM = SM.getFileManager();
+ PreprocessorOptions const* MaybePPOpts =
Info.Ctx.getCurrentPreprocessorOptions();
+ const std::vector<std::string>* MaybeSearchEntries = nullptr;
+ const std::vector<std::string> EmptySearchEntries(0);
+ FileID ThisFileID = SM.getFileID(Info.CurrentCall->CallRange.getBegin());
+ OptionalFileEntryRef ThisFile = std::nullopt;
+ if (ThisFileID.isValid()) {
+ ThisFile = SM.getFileEntryRefForID(ThisFileID);
+ }
+ if (MaybePPOpts) {
+ MaybeSearchEntries = &MaybePPOpts->EmbedEntries;
+ }
+ else {
+ MaybeSearchEntries = &EmptySearchEntries;
+ }
+ OptionalFileEntryRef ResourceFile =
+ LookupFileWithStdVec(ResourceName, false, true, FM, *MaybeSearchEntries,
ThisFile);
+ if (!ResourceFile) {
+ Info.FFDiag(ResourceNamePtrArg->getBeginLoc(),
diag::err_cannot_open_file) << ResourceName << "cannot find a matching resource
to open";
+ return false;
+ }
+ if (Info.Ctx.InputDependencyPatterns) {
+ StringRef ResourceSearchName =
ResourceFile->getFileEntry().tryGetRealPathName();
+ if (ResourceSearchName.empty()) {
+ ResourceSearchName = ResourceName;
+ }
+ if (!Info.Ctx.InputDependencyPatterns->Check(ResourceSearchName)) {
+ Info.FFDiag(ResourceNamePtrArg->getBeginLoc(),
diag::err_cannot_open_file) << ResourceName << "no '#depend' directive matches
the found resource";
+ return false;
+ }
+ }
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MaybeBinaryData =
+ FM.getBufferForFile(*ResourceFile, true, false, MaybeLimit, false);
+ if (auto Err = MaybeBinaryData.getError()) {
+ std::string ExtraMessage = Err.message();
+ std::string ErrorMessage = "could not open the resource";
+ if (!ExtraMessage.empty()) {
+ ErrorMessage += ", ";
+ ErrorMessage += ExtraMessage;
+ }
+ Info.FFDiag(ResourceNamePtrArg->getBeginLoc(),
diag::err_cannot_open_file) << ResourceName << ErrorMessage;
+ return false;
+ }
+ llvm::MemoryBuffer *BinaryData = MaybeBinaryData->get();
+ if (!BinaryData) {
+ Info.FFDiag(ResourceNamePtrArg->getBeginLoc(),
diag::err_cannot_open_file) << ResourceName << "found the resource but unable
to read the binary data";
+ return false;
+ }
+ size_t FullDataSize = BinaryData->getBufferSize();
+ DataSize = std::max<size_t>(0, std::min<size_t>(
+ DataOffset > FullDataSize
+ ? 0 : FullDataSize - DataOffset,
+ MaybeLimit ? *MaybeLimit : std::numeric_limits<size_t>::max()));
+
+ // Write out size
+ LValue SizeOutLVal;
+ if (!EvaluateLValue(SizeOutArg, SizeOutLVal, Info)) {
+ return Error(SizeOutArg);
+ }
+ APSInt BackingArraySizeVal(llvm::APInt(SizeTySize, DataSize, false), true);
+ APValue SizeOutResult(BackingArraySizeVal);
+ if (!handleAssignment(Info, SizeOutArg, SizeOutLVal,
SizeOutArg->getType(), SizeOutResult)) {
+ return Error(SizeOutArg);
+ }
+
+ // return data literal pointer to string
+ SourceLocation BuiltinLoc = E->getBeginLoc();
+ // TODO: perhaps there may be smarter ways to load this data up;
+ // right now, this is not doing too much on the face with caching or smart
+ // deduplication in the compiler.
+ StringRef TargetData(BinaryData->getBufferStart() + DataOffset, DataSize);
+ QualType BackingArrayTy = Info.Ctx.getConstantArrayType(
+ ArrElementTy, BackingArraySizeVal, nullptr, ArraySizeModifier::Normal,
0);
+ const ConstantArrayType *BackingArrayConstantArrayTy =
cast<ConstantArrayType>(BackingArrayTy);
+ StringLiteral *DataLiteral = StringLiteral::Create(Info.Ctx, TargetData,
+ StringLiteralKind::Binary, false, BackingArrayTy,
ArrayRef<SourceLocation>(&BuiltinLoc, 1));
+ if (!EvaluateLValue(DataLiteral, Result, Info)) {
+ return Error(E);
+ }
+ // inform the result we have put a string literal in there
+ Result.addArray(Info, E, BackingArrayConstantArrayTy);
+ return true;
+ }
default:
return false;
}
@@ -15284,8 +15508,10 @@ class IntExprEvaluator
bool Success(const llvm::APSInt &SI, const Expr *E, APValue &Result) {
assert(E->getType()->isIntegralOrEnumerationType() &&
"Invalid evaluation result.");
- assert(SI.isSigned() == E->getType()->isSignedIntegerOrEnumerationType() &&
- "Invalid evaluation result.");
+ auto Ty = E->getType();
+ if (SI.isSigned() != Ty->isSignedIntegerOrEnumerationType()) {
+ assert(false && "Invalid evaluation result.");
+ }
assert(SI.getBitWidth() == Info.Ctx.getIntWidth(E->getType()) &&
"Invalid evaluation result.");
Result = APValue(SI);
diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt
index adfc6ee326b5a..936e2798e65d7 100644
--- a/clang/lib/Basic/CMakeLists.txt
+++ b/clang/lib/Basic/CMakeLists.txt
@@ -71,6 +71,7 @@ add_clang_library(clangBasic
FileManager.cpp
FileSystemStatCache.cpp
IdentifierTable.cpp
+ InputDependencyCollection.cpp
LangOptions.cpp
LangStandards.cpp
MakeSupport.cpp
@@ -83,6 +84,7 @@ add_clang_library(clangBasic
ParsedAttrInfo.cpp
ProfileList.cpp
NoSanitizeList.cpp
+ ResourceSearch.cpp
SanitizerSpecialCaseList.cpp
Sanitizers.cpp
Sarif.cpp
diff --git a/clang/lib/Basic/IdentifierTable.cpp
b/clang/lib/Basic/IdentifierTable.cpp
index 7f96777fbd4cb..aa1a4cac6ec17 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -439,6 +439,7 @@ tok::PPKeywordKind IdentifierInfo::getPPKeywordID() const {
CASE( 6, 'a', 's', assert);
CASE( 6, 'd', 'f', define);
+ CASE( 6, 'd', 'p', depend);
CASE( 6, 'i', 'n', ifndef);
CASE( 6, 'i', 'p', import);
CASE( 6, 'm', 'd', module);
diff --git a/clang/lib/Basic/InputDependencyCollection.cpp
b/clang/lib/Basic/InputDependencyCollection.cpp
new file mode 100644
index 0000000000000..844b3694b10f5
--- /dev/null
+++ b/clang/lib/Basic/InputDependencyCollection.cpp
@@ -0,0 +1,181 @@
+//===--- InputDependencyCollection.h - Searching for Resource----*- C++
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// User-provided filters include/exclude profile instrumentation in certain
+// functions or files.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/InputDependencyCollection.h"
+#include "clang/Basic/FileManager.h"
+#include "llvm/Support/Path.h"
+#include <filesystem>
+
+using namespace llvm;
+
+namespace clang {
+
+PatternFilter::PatternFilter(std::string Pattern)
+ : Input(std::move(Pattern)), SearchRoot(""), PatternRoot(""), Pattern(""),
+ RootHandling(RootPatternScanType::None), Exported(false) {}
+
+bool PatternFilter::Check(StringRef Filename) const {
+ if (!PatternRoot.empty()) {
+ // if the pattern root is not empty, first check if the filename is
anchored
+ // in the pattern's root exit early if it's not
+ if (!Filename.contains(PatternRoot)) {
+ return false;
+ }
+ }
+ // otherwise, commit to doing a regex search
+ if (PatternRegex.match(Filename)) {
+ // it matches, we can find it
+ return true;
+ }
+ return false;
+}
+
+PatternFilter InputDependencyCollection::ComputeFilter(std::string Pattern,
+ bool Exported) {
+ static constexpr const char *RecursiveReplacement = ".*";
+ static constexpr const std::size_t RecursiveReplacementSize = 2;
+ static constexpr const char *Replacement = "[^/\\]*";
+ static constexpr const std::size_t ReplacementSize = 6;
+ PatternFilter Computed(std::move(Pattern));
+ // Technically, this is a very conservative estimate, since this is only in
+ // the most harmless of cases.
+ Computed.Pattern.reserve(Computed.Input.size());
+ const std::size_t InputSize = Computed.Input.size();
+ std::optional<std::size_t> LastRootSeparator = std::nullopt;
+ std::optional<std::size_t> LastStar = std::nullopt;
+ std::optional<std::size_t> LastStarStar = std::nullopt;
+ for (std::size_t I = 0; I < InputSize; ++I) {
+ const char CharVal = Computed.Input[I];
+ switch (CharVal) {
+ case '*':
+ if (I < InputSize && Computed.Input[I + 1] == '*') {
+ // completely unrestricted: replace with `.*`
+ ++I;
+ Computed.RootHandling = static_cast<RootPatternScanType>(
+ static_cast<unsigned int>(RootPatternScanType::RecursiveDirectory)
|
+ static_cast<unsigned int>(Computed.RootHandling));
+ Computed.Pattern.append(RecursiveReplacement,
RecursiveReplacementSize);
+ LastStarStar = I;
+ } else {
+ // regular non-path-delimited changers: `[^\\/]*
+ Computed.RootHandling = static_cast<RootPatternScanType>(
+ static_cast<unsigned int>(
+ LastStar
+ ? (*LastStar < LastRootSeparator
+ ?
RootPatternScanType::DirectoryAndRecursiveDirectory
+ : RootPatternScanType::Directory)
+ : RootPatternScanType::Directory) |
+ static_cast<unsigned int>(Computed.RootHandling));
+ Computed.Pattern.append(Replacement, ReplacementSize);
+ LastStar = I;
+ }
+ break;
+ case ')':
+ case '(':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case '^':
+ case '$':
+ case '.':
+ case '+':
+ case '?':
+ case '|':
+ // cases where the character must be escaped
+ Computed.Pattern.push_back('\\');
+ Computed.Pattern.push_back(CharVal);
+ break;
+ case '/':
+ if (Computed.RootHandling == RootPatternScanType::None)
+ LastRootSeparator = I;
+ Computed.Pattern.append("[/\\]", 4);
+ break;
+ case '\\':
+ if (Computed.RootHandling == RootPatternScanType::None)
+ LastRootSeparator = I;
+ Computed.Pattern.append("[/\\]", 4);
+ break;
+ default:
+ Computed.Pattern.push_back(CharVal);
+ break;
+ }
+ }
+ Computed.Pattern.push_back('$');
+ Computed.PatternRoot.append(
+ Computed.Input.cbegin(),
+ Computed.Input.cbegin() +
+ LastRootSeparator.value_or(static_cast<std::size_t>(0)));
+ // If LastSeperator == 0 and the Input's size is Non-Zero Could be a
directory
+ // OR a file we're relying on... could be a bit strange to work with!
+ // nevertheless, we'll treat it as a file, no reason to use `stat` and other
+ // temporary checks to try and determine whether or not something is a file
+ // versus a directory here.
+ Computed.PatternRegex = Regex(Computed.Pattern, Regex::NoFlags);
+ return Computed;
+}
+
+PatternFilter &
+InputDependencyCollection::Add(std::string Pattern, bool IsAngled,
+ bool Exported, FileManager &FM,
+ const std::vector<std::string> &SearchEntries,
+ OptionalFileEntryRef LookupFrom) {
+ PatternFilters.push_back(ComputeFilter(std::move(Pattern), Exported));
+ PatternFilter &Filter = PatternFilters.back();
+ if (Filter.PatternRoot.empty() ||
+ llvm::sys::path::is_absolute(Filter.PatternRoot)) {
+ // nothing else to do since it's absolute
+ // or the PatternRoot is empty, so we can't do any
+ // pre-culling.
+ return Filter;
+ }
+ // Find a plausible search root among the entries, if possible, to anchor
this
+ // to a given entry
+ auto TryDetermineSearchRoot = [&](StringRef SearchEntry) -> bool {
+ if (SearchEntry.contains(Filter.PatternRoot)) {
+ // the entry is contained within: approve the search entry as the search
+ // root
+ Filter.SearchRoot.assign(SearchEntry.begin(), SearchEntry.end());
+ return true;
+ }
+ return false;
+ };
+ if (!IsAngled && LookupFrom) {
+ // quote search; including the optional file entry as a root search
location
+ // too
+ StringRef LookupDirName = LookupFrom->getDir().getName();
+ if (TryDetermineSearchRoot(LookupDirName)) {
+ return Filter;
+ }
+ }
+ for (StringRef SearchEntry : SearchEntries) {
+ if (TryDetermineSearchRoot(SearchEntry)) {
+ return Filter;
+ }
+ }
+ // if we're here, then we just need to make the SearchRoot identical to the
+ // Pattern's root.
+ Filter.SearchRoot = Filter.PatternRoot;
+ return Filter;
+}
+
+bool InputDependencyCollection::Check(StringRef Filename) const {
+ for (const auto &Filter : PatternFilters) {
+ if (Filter.Check(Filename)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace clang
diff --git a/clang/lib/Basic/ResourceSearch.cpp
b/clang/lib/Basic/ResourceSearch.cpp
new file mode 100644
index 0000000000000..b9278d9b7f298
--- /dev/null
+++ b/clang/lib/Basic/ResourceSearch.cpp
@@ -0,0 +1,107 @@
+//===--- ResourceSearch.h - Searching for Resources -------------*- C++
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// User-provided filters include/exclude profile instrumentation in certain
+// functions or files.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/ResourceSearch.h"
+#include "clang/Basic/FileManager.h"
+
+namespace clang {
+
+template <typename Strings>
+OptionalFileEntryRef LookupFileWithImpl(StringRef Filename, bool isAngled,
+ bool OpenFile, FileManager &FM,
+ const Strings &SearchEntries,
+ OptionalFileEntryRef LookupFromFile) {
+ if (llvm::sys::path::is_absolute(Filename)) {
+ // lookup path or immediately fail
+ llvm::Expected<FileEntryRef> ShouldBeEntry = FM.getFileRef(
+ Filename, OpenFile, /*CacheFailure=*/true, /*IsText=*/false);
+ return llvm::expectedToOptional(std::move(ShouldBeEntry));
+ }
+
+ auto SeparateComponents = [](SmallVectorImpl<char> &LookupPath,
+ StringRef StartingFrom, StringRef FileName,
+ bool RemoveInitialFileComponentFromLookupPath) {
+ llvm::sys::path::native(StartingFrom, LookupPath);
+ if (RemoveInitialFileComponentFromLookupPath)
+ llvm::sys::path::remove_filename(LookupPath);
+ if (!LookupPath.empty() &&
+ !llvm::sys::path::is_separator(LookupPath.back())) {
+ LookupPath.push_back(llvm::sys::path::get_separator().front());
+ }
+ LookupPath.append(FileName.begin(), FileName.end());
+ };
+
+ // Otherwise, it's search time!
+ SmallString<512> LookupPath;
+ // Non-angled lookup
+ if (!isAngled) {
+ // Use file-based lookup.
+ if (LookupFromFile) {
+ SmallString<1024> TmpDir;
+ TmpDir = LookupFromFile->getDir().getName();
+ llvm::sys::path::append(TmpDir, Filename);
+ if (!TmpDir.empty()) {
+ llvm::Expected<FileEntryRef> ShouldBeEntry = FM.getFileRef(
+ TmpDir, OpenFile, /*CacheFailure=*/true, /*IsText=*/false);
+ if (ShouldBeEntry)
+ return llvm::expectedToOptional(std::move(ShouldBeEntry));
+ llvm::consumeError(ShouldBeEntry.takeError());
+ }
+ }
+
+ // Otherwise, do working directory lookup.
+ LookupPath.clear();
+ auto MaybeWorkingDirEntry = FM.getDirectoryRef(".");
+ if (MaybeWorkingDirEntry) {
+ DirectoryEntryRef WorkingDirEntry = *MaybeWorkingDirEntry;
+ StringRef WorkingDir = WorkingDirEntry.getName();
+ if (!WorkingDir.empty()) {
+ SeparateComponents(LookupPath, WorkingDir, Filename, false);
+ llvm::Expected<FileEntryRef> ShouldBeEntry = FM.getFileRef(
+ LookupPath, OpenFile, /*CacheFailure=*/true, /*IsText=*/false);
+ if (ShouldBeEntry)
+ return llvm::expectedToOptional(std::move(ShouldBeEntry));
+ llvm::consumeError(ShouldBeEntry.takeError());
+ }
+ }
+ }
+
+ for (const auto &Entry : SearchEntries) {
+ LookupPath.clear();
+ SeparateComponents(LookupPath, Entry, Filename, false);
+ llvm::Expected<FileEntryRef> ShouldBeEntry = FM.getFileRef(
+ LookupPath, OpenFile, /*CacheFailure=*/true, /*IsText=*/false);
+ if (ShouldBeEntry)
+ return llvm::expectedToOptional(std::move(ShouldBeEntry));
+ llvm::consumeError(ShouldBeEntry.takeError());
+ }
+ return std::nullopt;
+}
+
+OptionalFileEntryRef
+LookupFileWithStdVec(StringRef Filename, bool isAngled, bool OpenFile,
+ FileManager &FM,
+ const std::vector<std::string> &SearchEntries,
+ OptionalFileEntryRef LookupFromFile) {
+ return LookupFileWithImpl(Filename, isAngled, OpenFile, FM, SearchEntries,
+ LookupFromFile);
+}
+
+OptionalFileEntryRef LookupFileWith(StringRef Filename, bool isAngled,
+ bool OpenFile, FileManager &FM,
+ ArrayRef<StringRef> SearchEntries,
+ OptionalFileEntryRef LookupFromFile) {
+ return LookupFileWithImpl(Filename, isAngled, OpenFile, FM, SearchEntries,
+ LookupFromFile);
+}
+} // namespace clang
diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp
index 05ae1f348f920..450e44796cd6d 100644
--- a/clang/lib/Frontend/ASTUnit.cpp
+++ b/clang/lib/Frontend/ASTUnit.cpp
@@ -770,12 +770,13 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
/*IILookup=*/nullptr,
/*OwnsHeaderSearch=*/false);
- if (ToLoad >= LoadASTOnly)
+ if (ToLoad >= LoadASTOnly) {
AST->Ctx = llvm::makeIntrusiveRefCnt<ASTContext>(
*AST->LangOpts, AST->getSourceManager(), AST->PP->getIdentifierTable(),
AST->PP->getSelectorTable(), AST->PP->getBuiltinInfo(),
AST->getTranslationUnitKind());
-
+ AST->Ctx->InputDependencyPatterns = AST->PP->InputDependencyPatterns;
+ }
DisableValidationForModuleKind disableValid =
DisableValidationForModuleKind::None;
if (::getenv("LIBCLANG_DISABLE_PCH_VALIDATION"))
@@ -811,6 +812,9 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
// Initialize the ASTContext
AST->Ctx->InitBuiltinTypes(*AST->Target);
+ // Set preprocessor options
+ AST->Ctx->setCurrentPreprocessorOptions(AST->PP->getPreprocessorOpts());
+
// Adjust printing policy based on language options.
AST->Ctx->setPrintingPolicy(PrintingPolicy(*AST->LangOpts));
diff --git a/clang/lib/Frontend/CompilerInstance.cpp
b/clang/lib/Frontend/CompilerInstance.cpp
index a504cde306a35..59ca450c6091d 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -556,6 +556,8 @@ void CompilerInstance::createASTContext() {
getLangOpts(), PP.getSourceManager(), PP.getIdentifierTable(),
PP.getSelectorTable(), PP.getBuiltinInfo(), PP.TUKind);
Context->InitBuiltinTypes(getTarget(), getAuxTarget());
+ Context->setCurrentPreprocessorOptions(PP.getPreprocessorOpts());
+ Context->InputDependencyPatterns = PP.InputDependencyPatterns;
setASTContext(std::move(Context));
}
diff --git a/clang/lib/Frontend/DependencyFile.cpp
b/clang/lib/Frontend/DependencyFile.cpp
index 64629abcaeb52..379aed3a2d7f5 100644
--- a/clang/lib/Frontend/DependencyFile.cpp
+++ b/clang/lib/Frontend/DependencyFile.cpp
@@ -12,6 +12,7 @@
#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/Basic/FileManager.h"
+#include "clang/Basic/InputDependencyCollection.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/DependencyOutputOptions.h"
#include "clang/Frontend/Utils.h"
@@ -101,6 +102,90 @@ struct DepCollectorPPCallbacks : public PPCallbacks {
// Files that actually exist are handled by FileChanged.
}
+ enum IsRecursive : bool {
+ Flat = false,
+ Recursive = true
+ };
+
+ static inline void DoSearch(FileManager& FM, const PatternFilter& Filter,
DependencyCollector& DepCollector, StringRef Dir, IsRecursive Recurse) {
+ std::error_code EC;
+ for (llvm::vfs::directory_iterator FileIt =
FM.getVirtualFileSystem().dir_begin(Dir, EC), FileEnd = {};
+ FileIt != FileEnd && !EC; FileIt.increment(EC)) {
+ const auto &File = *FileIt;
+ switch (File.type()) {
+ case llvm::sys::fs::file_type::directory_file: {
+ if (Recurse) {
+ DoSearch(FM, Filter, DepCollector, File.path(), Recurse);
+ }
+ break;
+ }
+ case llvm::sys::fs::file_type::regular_file: {
+ StringRef Filename = File.path();
+ if (Filter.Check(Filename)) {
+ DepCollector.maybeAddDependency(Filename,
+ /*FromModule*/ false,
+ /*IsSystem*/ false,
+ /*IsModuleFile*/ false,
+ /*IsMissing*/ false);
+ }
+ break;
+ }
+ default:
+ // do nothing
+ break;
+ }
+ }
+ }
+
+ void DependDirective(SourceLocation HashLoc, const Token &DependTok,
+ StringRef Pattern, bool IsAngled,
+ const PatternFilter &Filter,
+ OptionalFileEntryRef CurrentFile) override {
+ if (Pattern.empty())
+ return;
+ FileManager& FM = PP.getFileManager();
+ llvm::vfs::FileSystem& VFS = FM.getVirtualFileSystem();
+ if (Filter.SearchRoot.empty()) {
+ // nothing we can do for ourselves here, just check the file directly and
+ // leave
+ if (Filter.RootHandling == RootPatternScanType::None) {
+ // it's either a directory or a file; add the dependency as-is
+ DepCollector.maybeAddDependency(Filter.Input,
+ /*FromModule*/ false,
+ /*IsSystem*/ false,
+ /*IsModuleFile*/ false,
+ /*IsMissing*/
!VFS.exists(Filter.Pattern));
+ return;
+ }
+ // if there's no search root, simply search from the current file's
+ // working directory (if it's quoted)
+ if (!IsAngled && CurrentFile) {
+ StringRef CurrentDir = CurrentFile->getDir().getName();
+ DoSearch(FM, Filter, DepCollector, CurrentDir, Filter.RootHandling ==
RootPatternScanType::Directory ? Flat : Recursive);
+ }
+ return;
+ }
+ // Otherwise, it's not empty. Note that we pre-computed the roots according
+ // to the compiler driver's resource/embed directory arguments previously,
+ // so we just need to do a singular check-if-exists-and-fire here.
+ if (Filter.RootHandling == RootPatternScanType::None) {
+ // treat as a single file, do a lookup and mark it for approval.
+ if (VFS.exists(Filter.Pattern)) {
+ // If it exists, it's what the user meant.
+ DepCollector.maybeAddDependency(Filter.Pattern,
+ /*FromModule*/ false,
+ /*IsSystem*/ false,
+ /*IsModuleFile*/ false,
+ /*IsMissing*/ false);
+ }
+ } else {
+ if (VFS.exists(Filter.PatternRoot)) {
+ DoSearch(FM, Filter, DepCollector, Filter.PatternRoot,
+ Filter.RootHandling == RootPatternScanType::Directory ? Flat :
Recursive);
+ }
+ }
+ }
+
void HasEmbed(SourceLocation, StringRef, bool,
OptionalFileEntryRef File) override {
if (!File)
@@ -160,8 +245,8 @@ struct DepCollectorASTListener : public ASTReaderListener {
/*IsSystem*/ false, /*IsModuleFile*/ true,
/*IsMissing*/ false);
}
- bool visitInputFile(StringRef Filename, bool IsSystem,
- bool IsOverridden, bool IsExplicitModule) override {
+ bool visitInputFile(StringRef Filename, bool IsSystem, bool IsOverridden,
+ bool IsExplicitModule) override {
if (IsOverridden || IsExplicitModule)
return true;
@@ -216,7 +301,7 @@ bool DependencyCollector::sawDependency(StringRef Filename,
bool FromModule,
(needSystemDependencies() || !IsSystem);
}
-DependencyCollector::~DependencyCollector() { }
+DependencyCollector::~DependencyCollector() {}
void DependencyCollector::attachToPreprocessor(Preprocessor &PP) {
PP.addPPCallbacks(std::make_unique<DepCollectorPPCallbacks>(*this, PP));
PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks(
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index b90c04776ff9e..b932f2b1dc3cf 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -19,6 +19,7 @@
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Module.h"
+#include "clang/Basic/ResourceSearch.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
@@ -1193,72 +1194,8 @@ OptionalFileEntryRef
Preprocessor::LookupEmbedFile(StringRef Filename,
bool isAngled,
bool OpenFile) {
FileManager &FM = this->getFileManager();
- if (llvm::sys::path::is_absolute(Filename)) {
- // lookup path or immediately fail
- llvm::Expected<FileEntryRef> ShouldBeEntry = FM.getFileRef(
- Filename, OpenFile, /*CacheFailure=*/true, /*IsText=*/false);
- return llvm::expectedToOptional(std::move(ShouldBeEntry));
- }
-
- auto SeparateComponents = [](SmallVectorImpl<char> &LookupPath,
- StringRef StartingFrom, StringRef FileName,
- bool RemoveInitialFileComponentFromLookupPath) {
- llvm::sys::path::native(StartingFrom, LookupPath);
- if (RemoveInitialFileComponentFromLookupPath)
- llvm::sys::path::remove_filename(LookupPath);
- if (!LookupPath.empty() &&
- !llvm::sys::path::is_separator(LookupPath.back())) {
- LookupPath.push_back(llvm::sys::path::get_separator().front());
- }
- LookupPath.append(FileName.begin(), FileName.end());
- };
-
- // Otherwise, it's search time!
- SmallString<512> LookupPath;
- // Non-angled lookup
- if (!isAngled) {
- OptionalFileEntryRef LookupFromFile =
getCurrentFileLexer()->getFileEntry();
- if (LookupFromFile) {
- // Use file-based lookup.
- SmallString<1024> TmpDir;
- TmpDir = LookupFromFile->getDir().getName();
- llvm::sys::path::append(TmpDir, Filename);
- if (!TmpDir.empty()) {
- llvm::Expected<FileEntryRef> ShouldBeEntry = FM.getFileRef(
- TmpDir, OpenFile, /*CacheFailure=*/true, /*IsText=*/false);
- if (ShouldBeEntry)
- return llvm::expectedToOptional(std::move(ShouldBeEntry));
- llvm::consumeError(ShouldBeEntry.takeError());
- }
- }
-
- // Otherwise, do working directory lookup.
- LookupPath.clear();
- auto MaybeWorkingDirEntry = FM.getDirectoryRef(".");
- if (MaybeWorkingDirEntry) {
- DirectoryEntryRef WorkingDirEntry = *MaybeWorkingDirEntry;
- StringRef WorkingDir = WorkingDirEntry.getName();
- if (!WorkingDir.empty()) {
- SeparateComponents(LookupPath, WorkingDir, Filename, false);
- llvm::Expected<FileEntryRef> ShouldBeEntry = FM.getFileRef(
- LookupPath, OpenFile, /*CacheFailure=*/true, /*IsText=*/false);
- if (ShouldBeEntry)
- return llvm::expectedToOptional(std::move(ShouldBeEntry));
- llvm::consumeError(ShouldBeEntry.takeError());
- }
- }
- }
-
- for (const auto &Entry : PPOpts.EmbedEntries) {
- LookupPath.clear();
- SeparateComponents(LookupPath, Entry, Filename, false);
- llvm::Expected<FileEntryRef> ShouldBeEntry = FM.getFileRef(
- LookupPath, OpenFile, /*CacheFailure=*/true, /*IsText=*/false);
- if (ShouldBeEntry)
- return llvm::expectedToOptional(std::move(ShouldBeEntry));
- llvm::consumeError(ShouldBeEntry.takeError());
- }
- return std::nullopt;
+ OptionalFileEntryRef LookupFromFileHere =
getCurrentFileLexer()->getFileEntry();
+ return LookupFileWithStdVec(Filename, isAngled, OpenFile, FM,
PPOpts.EmbedEntries, LookupFromFileHere);
}
//===----------------------------------------------------------------------===//
@@ -1359,6 +1296,7 @@ void Preprocessor::HandleDirective(Token &Result) {
case tok::pp___include_macros:
case tok::pp_pragma:
case tok::pp_embed:
+ case tok::pp_depend:
case tok::pp_module:
case tok::pp___preprocessed_module:
case tok::pp___preprocessed_import:
@@ -1493,6 +1431,8 @@ void Preprocessor::HandleDirective(Token &Result) {
return HandleIdentSCCSDirective(Result);
case tok::pp_embed:
return HandleEmbedDirective(Introducer.getLocation(), Result);
+ case tok::pp_depend:
+ return HandleDependDirective(Introducer.getLocation(), Result);
case tok::pp_assert:
//isExtension = true; // FIXME: implement #assert
break;
@@ -4181,6 +4121,62 @@ void Preprocessor::HandleEmbedDirective(SourceLocation
HashLoc,
HandleEmbedDirectiveImpl(HashLoc, *Params, BinaryContents, FilenameToGo);
}
+void Preprocessor::HandleDependDirective(SourceLocation HashLoc,
+ Token &DependTok) {
+ // Give the usual extension/compatibility warnings.
+ Diag(DependTok, diag::ext_pp_depend_directive)
+ << /*Clang*/ 1;
+ // It's always a clang extension: it will become a C++2c extension after
+ // more WG21 work later. Remember to come back and tweak this!
+
+ // this unfortunately has no meaning but it's part of the grammar for the
future so we should parse it normally
+ bool IsExported = false;
+ Token PatternTok;
+ Lex(PatternTok);
+ if (PatternTok.is(tok::kw_export)) {
+ IsExported = true;
+ }
+
+ // Parse the pattern header-name, with a potential first skip
+ if (LexHeaderName(PatternTok, true, !IsExported)) {
+ return;
+ }
+
+ if (PatternTok.isNot(tok::header_name)) {
+ Diag(PatternTok.getLocation(), diag::err_pp_expects_pattern);
+ if (PatternTok.isNot(tok::eod))
+ DiscardUntilEndOfDirective();
+ return;
+ }
+
+ Token EoDTok;
+ LexNonComment(EoDTok);
+ if (!EoDTok.isNot(tok::eod) && !EoDTok.isNot(tok::eof)) {
+ Diag(PatternTok.getLocation(), diag::err_pp_expected_eol);
+ DiscardUntilEndOfDirective();
+ return;
+ }
+
+
+ SmallString<256> PatternBuffer;
+ StringRef Pattern = getSpelling(PatternTok, PatternBuffer);
+ bool IsAngled =
+ GetIncludeFilenameSpelling(PatternTok.getLocation(), Pattern);
+ // Every pattern is local to where it was found, so prepend the current
+ // directory of the file if it's a non-absolute path from the perspective of
+ // this current header file
+ OptionalFileEntryRef CurrentFile = std::nullopt;
+ if (IsFileLexer()) {
+ PreprocessorLexer *ThisFileLexer = getCurrentFileLexer();
+ FileID ThisFID = ThisFileLexer->getFileID();
+ CurrentFile = SourceMgr.getFileEntryRefForID(ThisFID);
+ }
+ const PatternFilter& Filter
+ = InputDependencyPatterns->Add(Pattern.str(), IsAngled, IsExported,
getFileManager(), PPOpts.EmbedEntries, CurrentFile);
+ if (Callbacks)
+ Callbacks->DependDirective(HashLoc, DependTok, Pattern, IsAngled, Filter,
CurrentFile);
+}
+
/// HandleCXXImportDirective - Handle the C++ modules import directives
///
/// pp-import:
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index c430da67c1469..8f17af97f551b 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -1090,18 +1090,20 @@ void Preprocessor::LexTokensUntilEOF(std::vector<Token>
*Tokens) {
/// token is a '<').
/// \return \c true if we reached EOD or EOF while looking for a > token in
/// a concatenated header name and diagnosed it. \c false otherwise.
-bool Preprocessor::LexHeaderName(Token &FilenameTok, bool AllowMacroExpansion)
{
+bool Preprocessor::LexHeaderName(Token &FilenameTok, bool AllowMacroExpansion,
bool SkipFirst) {
// Lex using header-name tokenization rules if tokens are being lexed from
// a file. Just grab a token normally if we're in a macro expansion.
- if (CurPPLexer) {
- // Avoid nested header-name lexing when macro expansion recurses
- // __has_include(__has_include))
- if (CurPPLexer->ParsingFilename)
- LexUnexpandedToken(FilenameTok);
- else
- CurPPLexer->LexIncludeFilename(FilenameTok);
- } else {
- Lex(FilenameTok);
+ if (!SkipFirst) {
+ if (CurPPLexer) {
+ // Avoid nested header-name lexing when macro expansion recurses
+ // __has_include(__has_include))
+ if (CurPPLexer->ParsingFilename)
+ LexUnexpandedToken(FilenameTok);
+ else
+ CurPPLexer->LexIncludeFilename(FilenameTok);
+ } else {
+ Lex(FilenameTok);
+ }
}
// This could be a <foo/bar.h> file coming from a macro expansion. In this
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 1c1cd0f292753..e08ecc71dd19d 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3843,6 +3843,13 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl,
unsigned BuiltinID,
if (BuiltinCountedByRef(TheCall))
return ExprError();
break;
+ case Builtin::BI__builtin_std_embed:
+ // while we do not have Custom Typechecking,
+ // we have a `...` signature, so we do need to
+ // at least gently inspect some of the arguments
+ if (BuiltinStdEmbed(TheCall))
+ return ExprError();
+ break;
}
if (getLangOpts().HLSL && HLSL().CheckBuiltinFunctionCall(BuiltinID,
TheCall))
@@ -6668,6 +6675,150 @@ bool Sema::BuiltinCountedByRef(CallExpr *TheCall) {
return false;
}
+bool Sema::BuiltinStdEmbed(CallExpr *TheCall) {
+ const bool HasProperArgCount = !checkArgCountAtLeast(TheCall, 5);
+ const bool HasExtraLimitArg = !checkArgCountAtMost(TheCall, 6) &&
TheCall->getNumArgs() == 6;
+ if (!HasProperArgCount && !HasExtraLimitArg)
+ return true;
+
+ const Expr *SizeRef = TheCall->getArg(0);
+ const Expr *PtrRef = TheCall->getArg(1);
+ const unsigned int ResourceNameSizeIndex = 2;
+ const Expr *ResourceNameSize = TheCall->getArg(ResourceNameSizeIndex);
+ const unsigned int ResourceNamePtrIndex = 3;
+ const Expr *ResourceNamePtr = TheCall->getArg(ResourceNamePtrIndex);
+ const unsigned int OffsetIndex = 4;
+ const Expr *Offset = TheCall->getArg(OffsetIndex);
+ const unsigned int LimitIndex = 5;
+ const Expr *Limit = HasExtraLimitArg ? TheCall->getArg(LimitIndex) : nullptr;
+
+ // Size argument type
+ QualType SizeRefTy = SizeRef->getType();
+ if ((!SizeRefTy->isIntegralOrUnscopedEnumerationType())
+ || SizeRefTy.isConstant(Context)
+ || !SizeRef->isLValue()) {
+ Diag(TheCall->getBeginLoc(), diag::err_invalid_builtin_argument)
+ << SizeRef << "__builtin_std_embed"
+ << SizeRef->getSourceRange();
+ return true;
+ }
+
+ // value pointer, has to be non-constant (but pointer to `const`).
+ // tells us what the type for the builtin return is, serves no other purpose.
+ QualType PtrRefTy = PtrRef->getType();
+ if (!PtrRefTy->isPointerType()) {
+ Diag(TheCall->getBeginLoc(), diag::err_invalid_builtin_argument)
+ << PtrRefTy << "__builtin_std_embed"
+ << PtrRef->getSourceRange();
+ return true;
+ }
+ QualType ArrElementTy = PtrRefTy->getPointeeType();
+ if (!ArrElementTy.isConstant(Context)) {
+ Diag(TheCall->getBeginLoc(), diag::err_invalid_builtin_argument)
+ << PtrRef << "__builtin_std_embed"
+ << PtrRef->getSourceRange();
+ return true;
+ }
+
+ const uint64_t CharSize = Context.getCharWidth();
+ if (!(ArrElementTy->isIntegralOrEnumerationType()
+ && Context.getTypeSize(ArrElementTy) == CharSize
+ && Context.getTypeAlign(ArrElementTy) == CharSize)) {
+ Diag(TheCall->getBeginLoc(), diag::err_invalid_builtin_argument)
+ << PtrRef << "__builtin_std_embed"
+ << PtrRef->getSourceRange();
+ return true;
+ }
+
+ // Next argument is size of the string
+ const QualType SizeType = Context.getSizeType();
+ QualType ResourceNameSizeTy = ResourceNameSize->getType();
+ if (!ResourceNameSizeTy->isIntegralOrEnumerationType()) {
+ Expr *ResourceNameSizeMutable = const_cast<Expr *>(ResourceNameSize);
+ ExprResult ImplicitResourceNameSizeFixupResult =
PerformImplicitConversion(ResourceNameSizeMutable, SizeType,
AssignmentAction::Passing);
+ if (!ImplicitResourceNameSizeFixupResult.isUsable()) {
+ Diag(TheCall->getBeginLoc(),
diag::err_typecheck_converted_constant_expression)
+ << ResourceNameSizeTy << SizeType;
+ return true;
+ }
+ // The implicit conversion worked -- adjust the builtin's argument.
+ TheCall->setArg(ResourceNameSizeIndex,
ImplicitResourceNameSizeFixupResult.get());
+ }
+
+ // Pointer to an appropriate string type
+ // (char, wchar_t, or char8_t)
+ QualType ResourceNamePtrTy = ResourceNamePtr->getType();
+ if (!ResourceNamePtrTy->isPointerType()) {
+ const QualType AllowedTypes[3] = {
+ Context.getPointerType(Context.CharTy),
+ Context.getPointerType(Context.WCharTy),
+ Context.getPointerType(Context.Char8Ty)
+ };
+ Expr *ResourceNamePtrMutable = const_cast<Expr *>(ResourceNamePtr);
+ ExprResult ImplicitResourceNamePtrFixupResult;
+ for (const QualType& DesiredType : AllowedTypes) {
+ ImplicitResourceNamePtrFixupResult =
PerformImplicitConversion(ResourceNamePtrMutable, DesiredType,
AssignmentAction::Passing);
+ if (ImplicitResourceNamePtrFixupResult.isUsable()) {
+ break;
+ }
+ }
+ if (!ImplicitResourceNamePtrFixupResult.isUsable()) {
+ Diag(ResourceNamePtr->getBeginLoc(),
diag::err_typecheck_converted_constant_expression)
+ << ResourceNamePtrTy << "a pointer to char, wchar_t, or char8_t";
+ return true;
+ }
+ TheCall->setArg(ResourceNamePtrIndex,
ImplicitResourceNamePtrFixupResult.get());
+ ResourceNamePtr = TheCall->getArg(ResourceNamePtrIndex);
+ ResourceNamePtrTy = ResourceNamePtr->getType();
+ }
+ QualType
ResourceNameCharTy(ResourceNamePtrTy->getPointeeOrArrayElementType(), 0);
+ if (!ResourceNameCharTy->isCharType()
+ && !ResourceNameCharTy->isChar8Type()
+ && !ResourceNameCharTy->isWideCharType()) {
+ Diag(ResourceNamePtr->getBeginLoc(),
diag::err_typecheck_convert_incompatible_pointer)
+ << 1 << 1 << 1 << 0;
+ return true;
+
+ }
+
+ // Check the integer-convertible argument is offset
+ QualType OffsetTy = Offset->getType();
+ if (!OffsetTy->isIntegralOrEnumerationType()) {
+ Expr *OffsetMutable = const_cast<Expr *>(Offset);
+ ExprResult ImplicitOffsetFixupResult =
PerformImplicitConversion(OffsetMutable, SizeType, AssignmentAction::Passing);
+ if (!ImplicitOffsetFixupResult.isUsable()) {
+ Diag(TheCall->getBeginLoc(),
diag::err_typecheck_converted_constant_expression)
+ << ResourceNameSizeTy << SizeType;
+ return true;
+ }
+ // The implicit conversion worked -- adjust the builtin's argument.
+ TheCall->setArg(OffsetIndex, ImplicitOffsetFixupResult.get());
+ }
+
+ if (HasExtraLimitArg) {
+ // If present, final argument is offset
+ QualType LimitTy = Limit->getType();
+ if (!LimitTy->isIntegralOrEnumerationType()) {
+ Expr *LimitMutable = const_cast<Expr *>(Limit);
+ ExprResult ImplicitLimitFixupResult =
PerformImplicitConversion(LimitMutable, SizeType, AssignmentAction::Passing);
+ if (!ImplicitLimitFixupResult.isUsable()) {
+ Diag(TheCall->getBeginLoc(),
diag::err_typecheck_converted_constant_expression)
+ << ResourceNameSizeTy << SizeType;
+ return true;
+ }
+ // The implicit conversion worked -- adjust the builtin's argument.
+ TheCall->setArg(LimitIndex, ImplicitLimitFixupResult.get());
+ }
+ }
+
+ // return the same type as was put in; we don't actually do anything with
+ // the pointer-reference type other than to use it for
+ // proper typesetting.
+ TheCall->setType(PtrRefTy);
+
+ return false;
+}
+
/// The result of __builtin_counted_by_ref cannot be assigned to a variable.
/// It allows leaking and modification of bounds safety information.
bool Sema::CheckInvalidBuiltinCountedByRef(const Expr *E,
diff --git a/clang/tools/clang-import-test/clang-import-test.cpp
b/clang/tools/clang-import-test/clang-import-test.cpp
index 8e83687d3e96a..2234f44c0611a 100644
--- a/clang/tools/clang-import-test/clang-import-test.cpp
+++ b/clang/tools/clang-import-test/clang-import-test.cpp
@@ -229,6 +229,8 @@ BuildASTContext(CompilerInstance &CI, SelectorTable &ST,
Builtin::Context &BC) {
CI.getLangOpts(), CI.getSourceManager(),
PP.getIdentifierTable(), ST, BC, PP.TUKind);
AST->InitBuiltinTypes(CI.getTarget());
+ AST->setCurrentPreprocessorOptions(PP.getPreprocessorOpts());
+ AST->InputDependencyPatterns = PP.InputDependencyPatterns;
return AST;
}
diff --git a/clang/unittests/Lex/PPCallbacksTest.cpp
b/clang/unittests/Lex/PPCallbacksTest.cpp
index 9533fbc776e6e..f3b89db0be8c1 100644
--- a/clang/unittests/Lex/PPCallbacksTest.cpp
+++ b/clang/unittests/Lex/PPCallbacksTest.cpp
@@ -311,6 +311,7 @@ class PPCallbacksTest : public ::testing::Test {
ASTContext Context(OpenCLLangOpts, SourceMgr, PP.getIdentifierTable(),
PP.getSelectorTable(), PP.getBuiltinInfo(), PP.TUKind);
Context.InitBuiltinTypes(*Target);
+ Context.setCurrentPreprocessorOptions(PP.getPreprocessorOpts());
ASTConsumer Consumer;
Sema S(PP, Context, Consumer);
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 53165f0336b2d..c86b6e5288d7c 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1007,6 +1007,7 @@ set(files
cwchar
cwctype
deque
+ embed
errno.h
exception
execution
diff --git a/libcxx/include/embed b/libcxx/include/embed
new file mode 100644
index 0000000000000..e434433ff5b11
--- /dev/null
+++ b/libcxx/include/embed
@@ -0,0 +1,113 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_EMBED
+#define _LIBCPP_EMBED
+
+/*
+ embed synopsis
+
+namespace std {
+
+ template <typename T>
+consteval span<const T> embed(string_view resource_idenfier,
+ size_t offset = 0,
+ optional<size_t> limit = nullopt);
+
+ template <typename T>
+consteval span<const T> embed(wstring_view resource_idenfier,
+ size_t offset = 0,
+ optional<size_t> limit = nullopt);
+
+ template <typename T>
+consteval span<const T> embed(u8string_view resource_idenfier,
+ size_t offset = 0,
+ optional<size_t> limit = nullopt);
+
+} // namespace std
+
+*/
+
+#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+# include <__cxx03/__config>
+#else
+# include <__config>
+# include <__cstddef/byte.h>
+# include <__type_traits/is_enum.h>
+# include <__type_traits/is_integral.h>
+# include <__type_traits/is_same.h>
+# include <optional>
+# include <span>
+# include <string_view>
+# include <version>
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
+
+_LIBCPP_PUSH_MACROS
+# include <__undef_macros>
+
+# if !defined(_LIBCPP_HAS_EMBED)
+# if __has_builtin(__builtin_std_embed)
+# define _LIBCPP_HAS_EMBED 1
+# else
+# define _LIBCPP_HAS_EMBED 0
+# endif
+# endif
+
+# if _LIBCPP_HAS_EMBED != 0
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace __embed_detail {
+template <typename _Byte, typename _StrView>
+consteval span<const _Byte>
+__embed(const _StrView& __resource_identifier, size_t __offset = 0,
optional<size_t> __limit = nullopt) {
+ static_assert(sizeof(_Byte) == 1 && alignof(_Byte) == 1 &&
(is_integral<_Byte>::value || is_enum<_Byte>::value),
+ "embed data type must be a character type with a size and
alignment of 1");
+ size_t __size = 0;
+ _Byte const* __ptr = nullptr;
+ if (__limit) {
+ __ptr = __builtin_std_embed(
+ __size, __ptr, __resource_identifier.size(),
__resource_identifier.data(), __offset, *__limit);
+ } else {
+ __ptr = __builtin_std_embed(__size, __ptr, __resource_identifier.size(),
__resource_identifier.data(), __offset);
+ }
+ return span<const _Byte>(__ptr, __size);
+}
+} // namespace __embed_detail
+
+template <typename _Byte>
+consteval span<const _Byte>
+embed(string_view __resource_identifier, size_t __offset = 0, optional<size_t>
__limit = nullopt) {
+ return __embed_detail::__embed<_Byte>(__resource_identifier, __offset,
__limit);
+}
+
+template <typename _Byte>
+consteval span<const _Byte>
+embed(wstring_view __resource_identifier, size_t __offset = 0,
optional<size_t> __limit = nullopt) {
+ return __embed_detail::__embed<_Byte>(__resource_identifier, __offset,
__limit);
+}
+
+template <typename _Byte>
+consteval span<const _Byte>
+embed(u8string_view __resource_identifier, size_t __offset = 0,
optional<size_t> __limit = nullopt) {
+ return __embed_detail::__embed<_Byte>(__resource_identifier, __offset,
__limit);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+# endif
+
+_LIBCPP_POP_MACROS
+
+#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+
+#endif // _LIBCPP_EMBED
diff --git a/libcxx/include/version b/libcxx/include/version
index c43d36e569efb..e270a935fddcd 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -632,6 +632,13 @@ __cpp_lib_void_t
201411L <type_traits>
# define __cpp_lib_variant 202306L
#endif
+#if _LIBCPP_STD_VER >= 23
+# if __has_builtin(__builtin_std_embed) || (defined(_LIBCPP_HAS_EMBED) &&
(_LIBCPP_HAS_EMBED != 0))
+# define __cpp_lib_embed 202607L
+# endif
+#endif
+
+
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
// clang-format on
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits