diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index b69c616b009036..be69411cd74b34 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -4805,6 +4805,58 @@ class SourceLocExpr final : public Expr { friend class ASTStmtReader; }; +/// Represents a function call to __builtin_pp_embed(). +class PPEmbedExpr final : public Expr { + SourceLocation BuiltinLoc, RParenLoc; + DeclContext *ParentContext; + StringLiteral *Filename; + StringLiteral *BinaryData; + +public: + enum Action { + NotFound, + FoundOne, + Expanded, + }; + + PPEmbedExpr(const ASTContext &Ctx, QualType ResultTy, StringLiteral* Filename, StringLiteral* BinaryData, + SourceLocation BLoc, SourceLocation RParenLoc, + DeclContext *Context); + + /// Build an empty call expression. + explicit PPEmbedExpr(EmptyShell Empty) + : Expr(SourceLocExprClass, Empty) {} + + /// If the PPEmbedExpr has been resolved return the subexpression + /// representing the resolved value. Otherwise return null. + const DeclContext *getParentContext() const { return ParentContext; } + DeclContext *getParentContext() { return ParentContext; } + + SourceLocation getLocation() const { return BuiltinLoc; } + SourceLocation getBeginLoc() const { return BuiltinLoc; } + SourceLocation getEndLoc() const { return RParenLoc; } + + StringLiteral *getFilenameStringLiteral() const { return Filename; } + StringLiteral *getDataStringLiteral() const { return BinaryData; } + + size_t getDataElementCount(ASTContext &Context) const; + + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return const_child_range(child_iterator(), child_iterator()); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == PPEmbedExprClass; + } + +private: + friend class ASTStmtReader; +}; + /// Describes an C or C++ initializer list. /// /// InitListExpr describes an initializer list, which can be used to diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 3dd23eb38eeabf..6b7211bb0a0d3f 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2809,6 +2809,7 @@ DEF_TRAVERSE_STMT(ShuffleVectorExpr, {}) DEF_TRAVERSE_STMT(ConvertVectorExpr, {}) DEF_TRAVERSE_STMT(StmtExpr, {}) DEF_TRAVERSE_STMT(SourceLocExpr, {}) +DEF_TRAVERSE_STMT(PPEmbedExpr, {}) DEF_TRAVERSE_STMT(UnresolvedLookupExpr, { TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc())); diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index f2df283c74829f..4df86e35eebde3 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -59,6 +59,9 @@ def err_expected_string_literal : Error<"expected string literal " "'external_source_symbol' attribute|" "as argument of '%1' attribute}0">; +def err_builtin_pp_embed_invalid_argument : Error< + "invalid argument to '__builtin_pp_embed': %0">; + def err_invalid_string_udl : Error< "string literal with user-defined suffix cannot be used here">; def err_invalid_character_udl : Error< @@ -80,6 +83,9 @@ def err_expected : Error<"expected %0">; def err_expected_either : Error<"expected %0 or %1">; def err_expected_after : Error<"expected %1 after %0">; +def err_builtin_pp_embed_invalid_location : Error< + "'__builtin_pp_embed' in invalid location: %0%select{|%2}1">; + def err_param_redefinition : Error<"redefinition of parameter %0">; def warn_method_param_redefinition : Warning<"redefinition of method parameter %0">; def warn_method_param_declaration : Warning<"redeclaration of method parameter %0">, diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index cec301dfca2817..e3be997dd1c86e 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -203,6 +203,7 @@ def OpaqueValueExpr : StmtNode; def TypoExpr : StmtNode; def RecoveryExpr : StmtNode; def BuiltinBitCastExpr : StmtNode; +def PPEmbedExpr : StmtNode; // Microsoft Extensions. def MSPropertyRefExpr : StmtNode; diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 19a66fbb073119..167bd614efe7bd 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -154,10 +154,6 @@ 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) @@ -758,6 +754,7 @@ ALIAS("__char32_t" , char32_t , KEYCXX) KEYWORD(__builtin_bit_cast , KEYALL) KEYWORD(__builtin_available , KEYALL) KEYWORD(__builtin_sycl_unique_stable_name, KEYSYCL) +KEYWORD(__builtin_pp_embed , KEYALL) // Keywords defined by Attr.td. #ifndef KEYWORD_ATTRIBUTE @@ -993,6 +990,7 @@ ANNOTATION(repl_input_end) #undef CXX11_KEYWORD #undef KEYWORD #undef PUNCTUATOR +#undef BUILTINOK #undef TOK #undef C99_KEYWORD #undef C23_KEYWORD diff --git a/clang/include/clang/Lex/PPDirectiveParameter.h b/clang/include/clang/Lex/PPDirectiveParameter.h new file mode 100644 index 00000000000000..fc413c345adc53 --- /dev/null +++ b/clang/include/clang/Lex/PPDirectiveParameter.h @@ -0,0 +1,32 @@ +//===--- MacroArgs.h - Formal argument info for Macros ----------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the MacroArgs interface. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LEX_PPDIRECTIVEPARAMETER_H +#define LLVM_CLANG_LEX_PPDIRECTIVEPARAMETER_H + +#include "clang/Basic/SourceLocation.h" + +namespace clang { + +/// Captures basic information about a preprocessor directive parameter. +class PPDirectiveParameter { +public: + SourceLocation Start; + SourceLocation End; + + PPDirectiveParameter(SourceLocation Start, SourceLocation End) + : Start(Start), End(End) {} +}; + +} // end namespace clang + +#endif diff --git a/clang/include/clang/Lex/PPEmbedParameters.h b/clang/include/clang/Lex/PPEmbedParameters.h new file mode 100644 index 00000000000000..c2a01b80b61a80 --- /dev/null +++ b/clang/include/clang/Lex/PPEmbedParameters.h @@ -0,0 +1,78 @@ +//===--- MacroArgs.h - Formal argument info for Macros ----------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the MacroArgs interface. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LEX_PPEMBEDPARAMETERS_H +#define LLVM_CLANG_LEX_PPEMBEDPARAMETERS_H + +#include "clang/Lex/Token.h" +#include "clang/Lex/PPDirectiveParameter.h" +#include "llvm/ADT/SmallVector.h" + +namespace clang { + +/// Preprocessor extension embed parameter "clang::offset" +/// `clang::offset( constant-expression )` +class PPEmbedParameterOffset : public PPDirectiveParameter { +public: + size_t Offset; + + PPEmbedParameterOffset(size_t Offset, SourceLocation Start, SourceLocation End) + : Offset(Offset), PPDirectiveParameter(Start, End) {} +}; + +/// Preprocessor standard embed parameter "limit" +/// `limit( constant-expression )` +class PPEmbedParameterLimit : public PPDirectiveParameter { +public: + size_t Limit; + + PPEmbedParameterLimit(size_t Limit, SourceLocation Start, + SourceLocation End) + : Limit(Limit), PPDirectiveParameter(Start, End) {} +}; + +/// Preprocessor standard embed parameter "prefix" +/// `prefix( balanced-token-seq )` +class PPEmbedParameterPrefix : public PPDirectiveParameter { +public: + SmallVector Tokens; + + PPEmbedParameterPrefix(SmallVector Tokens, SourceLocation Start, + SourceLocation End) + : Tokens(std::move(Tokens)), PPDirectiveParameter(Start, End) {} +}; + +/// Preprocessor standard embed parameter "suffix" +/// `suffix( balanced-token-seq )` +class PPEmbedParameterSuffix : public PPDirectiveParameter { +public: + SmallVector Tokens; + + PPEmbedParameterSuffix(SmallVector Tokens, SourceLocation Start, + SourceLocation End) + : Tokens(std::move(Tokens)), PPDirectiveParameter(Start, End) {} +}; + +/// Preprocessor standard embed parameter "if_empty" +/// `if_empty( balanced-token-seq )` +class PPEmbedParameterIfEmpty : public PPDirectiveParameter { +public: + SmallVector Tokens; + + PPEmbedParameterIfEmpty(SmallVector Tokens, SourceLocation Start, + SourceLocation End) + : Tokens(std::move(Tokens)), PPDirectiveParameter(Start, End) {} +}; + +} // end namespace clang + +#endif diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 7470bf5882730c..517c61e870aac5 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -29,6 +29,7 @@ #include "clang/Lex/ModuleLoader.h" #include "clang/Lex/ModuleMap.h" #include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/PPEmbedParameters.h" #include "clang/Lex/Token.h" #include "clang/Lex/TokenLexer.h" #include "llvm/ADT/APSInt.h" @@ -1165,6 +1166,9 @@ class Preprocessor { void updateOutOfDateIdentifier(IdentifierInfo &II) const; + /// Buffers for used #embed directives + std::vector EmbedBuffers; + public: Preprocessor(std::shared_ptr PPOpts, DiagnosticsEngine &diags, const LangOptions &LangOpts, @@ -1735,15 +1739,15 @@ class Preprocessor { bool LexHeaderName(Token &Result, bool AllowMacroExpansion = true); struct LexEmbedParametersResult { - bool Successful; - std::optional MaybeLimitParam; - std::optional MaybeOffsetParam; - std::optional> MaybeIfEmptyParam; - std::optional> MaybePrefixParam; - std::optional> MaybeSuffixParam; - int UnrecognizedParams; + std::optional MaybeLimitParam; + std::optional MaybeOffsetParam; + std::optional MaybeIfEmptyParam; + std::optional MaybePrefixParam; + std::optional MaybeSuffixParam; SourceLocation StartLoc; SourceLocation EndLoc; + int UnrecognizedParams; + bool Successful; }; LexEmbedParametersResult LexEmbedParameters(Token &Current, @@ -1812,7 +1816,7 @@ class Preprocessor { /// Parses a simple integer literal to get its numeric value. Floating /// point literals and user defined literals are rejected. Used primarily to /// handle pragmas that accept integer arguments. - bool parseSimpleIntegerLiteral(Token &Tok, uint64_t &Value); + bool parseSimpleIntegerLiteral(Token &Tok, uint64_t &Value, bool WithLex = true); /// Disables macro expansion everywhere except for preprocessor directives. void SetMacroExpansionOnlyInDirectives() { @@ -2735,12 +2739,18 @@ class Preprocessor { // Binary data inclusion void HandleEmbedDirective(SourceLocation HashLoc, Token &Tok, const FileEntry *LookupFromFile = nullptr); - void HandleEmbedDirectiveNaive( - SourceLocation FilenameTok, LexEmbedParametersResult &Params, - StringRef BinaryContents, const size_t TargetCharWidth); - void HandleEmbedDirectiveBuiltin( - SourceLocation FilenameTok, LexEmbedParametersResult &Params, - StringRef BinaryContents, const size_t TargetCharWidth); + void HandleEmbedDirectiveNaive(SourceLocation HashLoc, + SourceLocation FilenameTok, + LexEmbedParametersResult &Params, + StringRef BinaryContents, + const size_t TargetCharWidth); + void HandleEmbedDirectiveBuiltin(SourceLocation HashLoc, + const Token &FilenameTok, + StringRef ResolvedFilename, + StringRef SearchPath, StringRef RelativePath, + LexEmbedParametersResult &Params, + StringRef BinaryContents, + const size_t TargetCharWidth); // File inclusion. void HandleIncludeDirective(SourceLocation HashLoc, Token &Tok, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1c88855a73970d..00709555cedbf3 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5983,6 +5983,10 @@ class Sema final { ArrayRef Arg, SourceLocation RParenLoc, Expr *Config = nullptr, bool IsExecConfig = false, ADLCallKind UsesADL = ADLCallKind::NotADL); + /// `Fn` may be a null pointer. + void ModifyCallExprArguments(Expr *Fn, SourceLocation LParenLoc, + SmallVectorImpl &ArgExprs, + SourceLocation RParenLoc); ExprResult ActOnCUDAExecConfigExpr(Scope *S, SourceLocation LLLLoc, MultiExprArg ExecConfig, @@ -6100,6 +6104,34 @@ class Sema final { SourceLocation BuiltinLoc, SourceLocation RPLoc); + // __builtin_pp_embed() + ExprResult ActOnPPEmbedExpr(SourceLocation BuiltinLoc, + SourceLocation Base64DataLocation, + SourceLocation RPLoc, StringLiteral *Filename, + QualType DataTy, std::vector BinaryData); + + IntegerLiteral *ExpandSinglePPEmbedExpr(PPEmbedExpr *PPEmbed); + + PPEmbedExpr::Action + CheckExprListForPPEmbedExpr(ArrayRef ExprList, + std::optional MaybeInitType); + PPEmbedExpr::Action + ExpandPPEmbedExprInExprList(ArrayRef ExprList, + SmallVectorImpl &OutputExprList, + bool ClearOutputFirst = true); + PPEmbedExpr::Action + ExpandPPEmbedExprInExprList(SmallVectorImpl &OutputList); + + enum PPEmbedExprContext { + PPEEC__StaticAssert, + PPEEC_StaticAssert, + }; + + StringRef GetLocationName(PPEmbedExprContext Context) const; + + bool DiagnosePPEmbedExpr(Expr *&E, SourceLocation ContextLocation, + PPEmbedExprContext Context, bool SingleAllowed = true); + // Build a potentially resolved SourceLocExpr. ExprResult BuildSourceLocExpr(SourceLocExpr::IdentKind Kind, QualType ResultTy, SourceLocation BuiltinLoc, @@ -8292,6 +8324,10 @@ class Sema final { SourceLocation EqualLoc, ParsedTemplateArgument DefaultArg); + void ModifyTemplateArguments( + const TemplateTy &Template, + SmallVectorImpl &TemplateArgs); + TemplateParameterList * ActOnTemplateParameterList(unsigned Depth, SourceLocation ExportLoc, diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 5c32fbc079c9a6..138c52bc8149fc 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1715,6 +1715,9 @@ enum StmtCode { /// A SourceLocExpr record. EXPR_SOURCE_LOC, + /// A PPEmbedExpr record. + EXPR_BUILTIN_PP_EMBED, + /// A ShuffleVectorExpr record. EXPR_SHUFFLE_VECTOR, diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 5d3b510df1ef9b..cdee8375890d30 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2329,6 +2329,22 @@ APValue SourceLocExpr::EvaluateInContext(const ASTContext &Ctx, llvm_unreachable("unhandled case"); } +PPEmbedExpr::PPEmbedExpr(const ASTContext &Ctx, QualType ResultTy, + StringLiteral *Filename, + StringLiteral *BinaryData, + SourceLocation BLoc, + SourceLocation RParenLoc, + DeclContext *ParentContext) + : Expr(PPEmbedExprClass, ResultTy, VK_PRValue, OK_Ordinary), + BuiltinLoc(BLoc), RParenLoc(RParenLoc), ParentContext(ParentContext), Filename(Filename), BinaryData(BinaryData) { + setDependence(ExprDependence::None); +} + +size_t PPEmbedExpr::getDataElementCount(ASTContext &Context) const { + return getDataStringLiteral()->getByteLength() / + (Context.getTypeSize(getType()) / Context.getTypeSize(Context.CharTy)); +} + InitListExpr::InitListExpr(const ASTContext &C, SourceLocation lbraceloc, ArrayRef initExprs, SourceLocation rbraceloc) : Expr(InitListExprClass, QualType(), VK_PRValue, OK_Ordinary), @@ -3547,6 +3563,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case CXXUuidofExprClass: case OpaqueValueExprClass: case SourceLocExprClass: + case PPEmbedExprClass: case ConceptSpecializationExprClass: case RequiresExprClass: case SYCLUniqueStableNameExprClass: diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index ffa7c6802ea6e1..ddb0dfc498cf9a 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -204,6 +204,10 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::RequiresExprClass: return Cl::CL_PRValue; + case Expr::PPEmbedExprClass: + // Nominally, this just goes through as a PRValue until we actually expand it and check it. + return Cl::CL_PRValue; + // Make HLSL this reference-like case Expr::CXXThisExprClass: return Lang.HLSL ? Cl::CL_LValue : Cl::CL_PRValue; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 5a33e918db8e8c..804c56671aac93 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8921,6 +8921,11 @@ class PointerExprEvaluator return true; } + bool VisitPPEmbedExpr(const PPEmbedExpr *E) { + llvm_unreachable("Not yet implemented for ExprConstant.cpp"); + return true; + } + bool VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E) { std::string ResultStr = E->ComputeName(Info.Ctx); @@ -16155,6 +16160,9 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { return ICEDiag(IK_NotICE, E->getBeginLoc()); return CheckICE(cast(E)->getSubExpr(), Ctx); } + case Expr::PPEmbedExprClass: { + return ICEDiag(IK_ICE, E->getBeginLoc()); + } } llvm_unreachable("Invalid StmtClass!"); diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 23ec35cae4b7b4..f08fb766efd777 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4721,6 +4721,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, case Expr::PseudoObjectExprClass: case Expr::AtomicExprClass: case Expr::SourceLocExprClass: + case Expr::PPEmbedExprClass: case Expr::BuiltinBitCastExprClass: { NotPrimaryExpr(); diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index a31aa0cfeeed8d..f94386be778847 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -49,6 +49,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Base64.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" @@ -1145,6 +1146,12 @@ void StmtPrinter::VisitSourceLocExpr(SourceLocExpr *Node) { OS << Node->getBuiltinStr() << "()"; } +void StmtPrinter::VisitPPEmbedExpr(PPEmbedExpr *Node) { + OS << "__builtin_pp_embed(" << Node->getType() << ", " + << Node->getFilenameStringLiteral()->getBytes() << ", \"" + << llvm::encodeBase64(Node->getDataStringLiteral()->getBytes()) << "\")"; +} + void StmtPrinter::VisitConstantExpr(ConstantExpr *Node) { PrintExpr(Node->getSubExpr()); } diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 22b6855b0fff23..b70d4d925cc986 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2284,6 +2284,10 @@ void StmtProfiler::VisitSourceLocExpr(const SourceLocExpr *E) { VisitExpr(E); } +void StmtProfiler::VisitPPEmbedExpr(const PPEmbedExpr *E) { + VisitExpr(E); +} + void StmtProfiler::VisitRecoveryExpr(const RecoveryExpr *E) { VisitExpr(E); } void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) { diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp index fb9baa92e6836d..15f6d183cda09b 100644 --- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -898,6 +898,10 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok, std::string Name = M->getFullModuleName(); Callbacks->OS->write(Name.data(), Name.size()); Callbacks->HandleNewlinesInToken(Name.data(), Name.size()); + } else if (Tok.is(tok::comma)) { + // hard-wire comma writing to prevent #embed from spilling unread contents + // from fast token dumping or builtin speed writing + OS.write(','); } else if (Tok.isAnnotation()) { // Ignore annotation tokens created by pragmas - the pragmas themselves // will be reproduced in the preprocessed output. diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 7968c62cbd3e7b..e2e55daa77b854 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -566,6 +566,7 @@ class RuntimeInterfaceBuilder CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)Ty.getAsOpaquePtr()); // The QualType parameter `OpaqueType`, represented as `void*`. Args.push_back(TypeArg); + S.ModifyCallExprArguments(nullptr, E->getBeginLoc(), Args, E->getEndLoc()); // We push the last parameter based on the type of the Expr. Note we need // special care for rvalue struct. diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index feed1b9ecd71a8..b55b4c360d4429 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -417,6 +417,14 @@ unsigned Lexer::getSpelling(const Token &Tok, const char *&Buffer, } } + // NOTE: this is to prevent a few cases where token streams with + // commas are used to print with pseudo-locations after a faux-expansion + // cause reading a bogus location from a source file that does not exist. + if (Tok.is(tok::comma)) { + Buffer = ","; + return 1; + } + // NOTE: this can be checked even after testing for an IdentifierInfo. if (Tok.isLiteral()) TokStart = Tok.getLiteralData(); diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index e0d98d7ca03fa1..21f158bb505856 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -42,11 +42,13 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/AlignOf.h" +#include "llvm/Support/Base64.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Path.h" #include "llvm/Support/SaveAndRestore.h" #include #include +#include #include #include #include @@ -3631,10 +3633,12 @@ Preprocessor::LexEmbedParameters(Token &CurTok, bool InHasEmbed, SmallVector ParameterTokens; tok::TokenKind EndTokenKind = InHasEmbed ? tok::r_paren : tok::eod; Result.StartLoc = CurTok.getLocation(); + Result.EndLoc = CurTok.getLocation(); for (LexNonComment(CurTok); CurTok.isNot(EndTokenKind);) { Parameter.clear(); // Lex identifier [:: identifier ...] if (!CurTok.is(tok::identifier)) { + Result.EndLoc = CurTok.getEndLoc(); Diag(CurTok, diag::err_expected) << "identifier"; DiscardUntilEndOfDirective(); return Result; @@ -3647,6 +3651,7 @@ Preprocessor::LexEmbedParameters(Token &CurTok, bool InHasEmbed, Parameter.append("::"); LexNonComment(CurTok); if (!CurTok.is(tok::identifier)) { + Result.EndLoc = CurTok.getEndLoc(); Diag(CurTok, diag::err_expected) << "identifier"; DiscardUntilEndOfDirective(); return Result; @@ -3670,25 +3675,19 @@ Preprocessor::LexEmbedParameters(Token &CurTok, bool InHasEmbed, return Result; } const llvm::APSInt &LimitResult = *LimitEvalResult.Value; - const bool ValueDoesNotFit = - LimitResult.getBitWidth() > 64 - ? true - : (LimitResult.isUnsigned() || - (LimitResult.isSigned() && LimitResult.isNegative())); - if (ValueDoesNotFit) { + if (LimitResult.getBitWidth() > 64) { Diag(CurTok, diag::warn_pp_expr_overflow); - // just truncate and roll with that, I guess? - Result.MaybeLimitParam = - static_cast(LimitResult.getRawData()[0]); - } else { - Result.MaybeLimitParam = - static_cast(LimitResult.getZExtValue()); } + size_t LimitValue = 0; + LimitValue = LimitResult.getLimitedValue(); + Result.MaybeLimitParam = PPEmbedParameterLimit{ + LimitValue, ParameterStartTok.getLocation(), CurTok.getEndLoc()}; LexNonComment(CurTok); } else if (Parameter == "clang::offset") { // we have a limit parameter and its internals are processed using // evaluation rules from #if - handle here if (CurTok.isNot(tok::l_paren)) { + Result.EndLoc = CurTok.getEndLoc(); Diag(CurTok, diag::err_pp_expected_after) << "(" << Parameter; DiscardUntilEndOfDirective(); return Result; @@ -3697,18 +3696,17 @@ Preprocessor::LexEmbedParameters(Token &CurTok, bool InHasEmbed, DirectiveEvalResult OffsetEvalResult = EvaluateDirectiveExpression(ParameterIfNDef, CurTok, false, true); if (!OffsetEvalResult.Value) { + Result.EndLoc = CurTok.getEndLoc(); return Result; } const llvm::APSInt &OffsetResult = *OffsetEvalResult.Value; + size_t OffsetValue; if (OffsetResult.getBitWidth() > 64) { Diag(CurTok, diag::warn_pp_expr_overflow); - // just truncate and roll with that, I guess? - Result.MaybeOffsetParam = - static_cast(OffsetResult.getRawData()[0]); - } else { - Result.MaybeOffsetParam = - static_cast(OffsetResult.getZExtValue()); } + OffsetValue = OffsetResult.getLimitedValue(); + Result.MaybeOffsetParam = PPEmbedParameterOffset{ + OffsetValue, ParameterStartTok.getLocation(), CurTok.getEndLoc()}; LexNonComment(CurTok); } else { if (CurTok.is(tok::l_paren)) { @@ -3764,6 +3762,7 @@ Preprocessor::LexEmbedParameters(Token &CurTok, bool InHasEmbed, return true; }; if (!ParseArgToken()) { + Result.EndLoc = CurTok.getEndLoc(); return Result; } if (!CurTok.is(tok::r_paren)) { @@ -3775,14 +3774,17 @@ Preprocessor::LexEmbedParameters(Token &CurTok, bool InHasEmbed, } // "Token-soup" parameters if (Parameter == "if_empty") { - // TODO: integer list optimization - Result.MaybeIfEmptyParam = std::move(ParameterTokens); + Result.MaybeIfEmptyParam = PPEmbedParameterIfEmpty{ + std::move(ParameterTokens), ParameterStartTok.getLocation(), + CurTok.getLocation()}; } else if (Parameter == "prefix") { - // TODO: integer list optimization - Result.MaybePrefixParam = std::move(ParameterTokens); + Result.MaybePrefixParam = PPEmbedParameterPrefix{ + std::move(ParameterTokens), ParameterStartTok.getLocation(), + CurTok.getLocation()}; } else if (Parameter == "suffix") { - // TODO: integer list optimization - Result.MaybeSuffixParam = std::move(ParameterTokens); + Result.MaybeSuffixParam = PPEmbedParameterSuffix{ + std::move(ParameterTokens), ParameterStartTok.getLocation(), + CurTok.getLocation()}; } else { ++Result.UnrecognizedParams; if (DiagnoseUnknown) { @@ -3793,6 +3795,7 @@ Preprocessor::LexEmbedParameters(Token &CurTok, bool InHasEmbed, } } Result.Successful = true; + Result.EndLoc = CurTok.getEndLoc(); return Result; } @@ -3823,89 +3826,255 @@ inline constexpr const char *IntegerLiterals[] = { "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"}; -void Preprocessor::HandleEmbedDirectiveNaive(SourceLocation FilenameLoc, - LexEmbedParametersResult &Params, - StringRef BinaryContents, - const size_t TargetCharWidth) { - (void)TargetCharWidth; // for later, when we support various sizes +void Preprocessor::HandleEmbedDirectiveNaive(SourceLocation HashLoc, + SourceLocation FilenameLoc, + LexEmbedParametersResult &Params, + StringRef BinaryContents, + const size_t TargetCharWidth) { + // Load up a new embed buffer for this file and set of parameters in + // particular. + EmbedBuffers.push_back(""); + size_t EmbedBufferNumber = EmbedBuffers.size(); + std::string EmbedBufferNumberVal = std::to_string(EmbedBufferNumber); + llvm::Twine EmbedBufferName = [](const std::string &Number) { + llvm::Twine PrefixNumber = (""); + }(EmbedBufferNumberVal); + std::string &TargetEmbedBuffer = EmbedBuffers.back(); + + // In the future, this might improve. + const StringRef SmallestType = "unsigned char"; + + // Generate the look-alike source file + if (BinaryContents.empty()) { + if (Params.MaybeIfEmptyParam) { + PPEmbedParameterIfEmpty &EmptyParam = *Params.MaybeIfEmptyParam; + for (const auto &Tok : EmptyParam.Tokens) { + TargetEmbedBuffer.append(this->getSpelling(Tok)); + } + } + } else { + if (Params.MaybePrefixParam) { + PPEmbedParameterPrefix &PrefixParam = *Params.MaybePrefixParam; + for (const auto &Tok : PrefixParam.Tokens) { + TargetEmbedBuffer.append(this->getSpelling(Tok)); + } + } + for (size_t I = 0; I < BinaryContents.size(); ++I) { + unsigned char ByteValue = BinaryContents[I]; + StringRef ByteRepresentation = IntegerLiterals[ByteValue]; + TargetEmbedBuffer.append(2, '('); + TargetEmbedBuffer.append(SmallestType.data(), SmallestType.size()); + TargetEmbedBuffer.append(1, ')'); + TargetEmbedBuffer.append(ByteRepresentation.data(), + ByteRepresentation.size()); + TargetEmbedBuffer.append(1, ')'); + bool AtEndOfContents = I == (BinaryContents.size() - 1); + if (!AtEndOfContents) { + TargetEmbedBuffer.append(1, ','); + } + } + if (Params.MaybeSuffixParam) { + PPEmbedParameterSuffix &SuffixParam = *Params.MaybeSuffixParam; + for (const auto &Tok : SuffixParam.Tokens) { + TargetEmbedBuffer.append(this->getSpelling(Tok)); + } + } + } + + // Create faux-file and its ID, backed by a memory buffer. + std::unique_ptr EmbedMemBuffer = + llvm::MemoryBuffer::getMemBufferCopy(TargetEmbedBuffer, EmbedBufferName); + assert(EmbedMemBuffer && "Cannot create predefined source buffer"); + FileID EmbedBufferFID = SourceMgr.createFileID(std::move(EmbedMemBuffer)); + assert(EmbedBufferFID.isValid() && + "Could not create FileID for #embed directive?"); + // Start parsing the look-alike source file for the embed directive and + // pretend everything is normal + // TODO: (Maybe? )Stop the PPCallbacks from considering this a Real File™. + EnterSourceFile(EmbedBufferFID, nullptr, HashLoc, false); +} + +static bool TokenListIsCharacterArray(Preprocessor &PP, + const size_t TargetCharWidth, + bool IsPrefix, + SmallVectorImpl &Tokens, + llvm::SmallVectorImpl &Output) { + const bool IsSuffix = !IsPrefix; + size_t MaxValue = + static_cast(std::pow((size_t)2, TargetCharWidth)) - 1u; size_t TokenIndex = 0; - const size_t InitListTokensSize = [&]() { - if (BinaryContents.empty()) { - if (Params.MaybeIfEmptyParam) { - return Params.MaybeIfEmptyParam->size(); - } else { - return static_cast(0); + // if it's a suffix, we are expecting a comma first + // if it's a prefix, we are expecting a numeric literal first + bool ExpectingNumericLiteral = IsPrefix; + const size_t TokensSize = Tokens.size(); + if (Tokens.empty()) { + return true; + } + for (; TokenIndex < TokensSize; + (void)++TokenIndex, ExpectingNumericLiteral = !ExpectingNumericLiteral) { + const Token &Tok = Tokens[TokenIndex]; + // TODO: parse an optional, PLAIN `(unsigned char)` cast in front of the + // literals, since the Spec technically decrees each element is of type + // `unsigned char` (unless we have a potential future extension for + // `clang::type(meow)` as an embed parameter + if (ExpectingNumericLiteral) { + if (Tok.isNot(tok::numeric_constant)) { + return false; + } + uint64_t Value = {}; + Token ParsingTok = Tok; + if (!PP.parseSimpleIntegerLiteral(ParsingTok, Value, false)) { + // numeric literal is a floating point literal or a UDL; too complex for + // us + return false; + } + if (Value > MaxValue || Value > static_cast(0xFF)) { + // number is too large + return false; } + Output.push_back((char)Value); } else { - return static_cast( - (Params.MaybePrefixParam ? Params.MaybePrefixParam->size() : 0) + - (BinaryContents.size() * 2 - 1) + - (Params.MaybeSuffixParam ? Params.MaybeSuffixParam->size() : 0)); + if (Tok.isNot(tok::comma)) { + return false; + } } - }(); - std::unique_ptr InitListTokens(new Token[InitListTokensSize]()); + } + const bool EndedOnNumber = !ExpectingNumericLiteral; + if (IsPrefix && EndedOnNumber) { + // we ended on a number: this is a failure for prefix! + return false; + } + const bool EndedOnComma = ExpectingNumericLiteral; + if (IsSuffix && EndedOnComma) { + // we ended on a comma: this is a failure for suffix! + return false; + } + // if all tokens have been consumed by the above process, then we have + // succeeded. + return TokenIndex == TokensSize; +} - if (BinaryContents.empty()) { - if (Params.MaybeIfEmptyParam) { - std::copy(Params.MaybeIfEmptyParam->begin(), - Params.MaybeIfEmptyParam->end(), InitListTokens.get()); - TokenIndex += Params.MaybeIfEmptyParam->size(); - assert(TokenIndex == InitListTokensSize); - EnterTokenStream(std::move(InitListTokens), InitListTokensSize, true, - true); +static void TripleEncodeBase64(StringRef Bytes0, StringRef Bytes1, + StringRef Bytes2, std::string &OutputBuffer) { + static const char Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + const size_t TotalSize = Bytes0.size() + Bytes1.size() + Bytes2.size(); + const size_t Bytes0Size = Bytes0.size(); + const size_t Bytes01Size = Bytes0.size() + Bytes1.size(); + const size_t IndexOffset = OutputBuffer.size(); + OutputBuffer.resize(OutputBuffer.size() + (((TotalSize + 2) / 3) * 4)); + auto IndexInto = [&](size_t i) -> unsigned char { + if (i >= Bytes0Size) { + if (i >= Bytes01Size) { + return Bytes2[i - Bytes01Size]; + } + return Bytes1[i - Bytes0Size]; } - return; + return Bytes0[i]; + }; + + size_t i = 0, j = 0; + for (size_t n = TotalSize / 3 * 3; i < n; i += 3, j += 4) { + uint32_t x = ((unsigned char)IndexInto(i) << 16) | + ((unsigned char)IndexInto(i + 1) << 8) | + (unsigned char)IndexInto(i + 2); + OutputBuffer[IndexOffset + j + 0] = Table[(x >> 18) & 63]; + OutputBuffer[IndexOffset + j + 1] = Table[(x >> 12) & 63]; + OutputBuffer[IndexOffset + j + 2] = Table[(x >> 6) & 63]; + OutputBuffer[IndexOffset + j + 3] = Table[x & 63]; + } + if (i + 1 == TotalSize) { + uint32_t x = ((unsigned char)IndexInto(i) << 16); + OutputBuffer[IndexOffset + j + 0] = Table[(x >> 18) & 63]; + OutputBuffer[IndexOffset + j + 1] = Table[(x >> 12) & 63]; + OutputBuffer[IndexOffset + j + 2] = '='; + OutputBuffer[IndexOffset + j + 3] = '='; + } else if (i + 2 == TotalSize) { + uint32_t x = ((unsigned char)IndexInto(i) << 16) | + ((unsigned char)IndexInto(i + 1) << 8); + OutputBuffer[IndexOffset + j + 0] = Table[(x >> 18) & 63]; + OutputBuffer[IndexOffset + j + 1] = Table[(x >> 12) & 63]; + OutputBuffer[IndexOffset + j + 2] = Table[(x >> 6) & 63]; + OutputBuffer[IndexOffset + j + 3] = '='; } +} - // FIXME: this does not take the target's byte size into account; - // will fail on many DSPs and embedded machines! +void Preprocessor::HandleEmbedDirectiveBuiltin( + SourceLocation HashLoc, const Token &FilenameTok, + StringRef ResolvedFilename, StringRef SearchPath, StringRef RelativePath, + LexEmbedParametersResult &Params, StringRef BinaryContents, + const size_t TargetCharWidth) { + // if it's empty, just process it like a normal expanded token stream + if (BinaryContents.empty()) { + HandleEmbedDirectiveNaive(HashLoc, FilenameTok.getLocation(), Params, + BinaryContents, TargetCharWidth); + return; + } + SmallVector BinaryPrefix{}; + SmallVector BinarySuffix{}; if (Params.MaybePrefixParam) { - std::copy(Params.MaybePrefixParam->begin(), Params.MaybePrefixParam->end(), - InitListTokens.get() + TokenIndex); - TokenIndex += Params.MaybePrefixParam->size(); - } - for (size_t I = 0; I < BinaryContents.size(); ++I) { - unsigned char ByteValue = BinaryContents[I]; - StringRef ByteRepresentation = IntegerLiterals[ByteValue]; - const size_t InitListIndex = TokenIndex; - Token &IntToken = InitListTokens[InitListIndex]; - IntToken.setKind(tok::numeric_constant); - IntToken.setLiteralData(ByteRepresentation.data()); - IntToken.setLength(ByteRepresentation.size()); - IntToken.setLocation(FilenameLoc); - ++TokenIndex; - bool AtEndOfContents = I == (BinaryContents.size() - 1); - if (!AtEndOfContents) { - const size_t CommaInitListIndex = InitListIndex + 1; - Token &CommaToken = InitListTokens[CommaInitListIndex]; - CommaToken.setKind(tok::comma); - CommaToken.setLocation(FilenameLoc); - ++TokenIndex; + // If we ahve a prefix, validate that it's a good fit for direct data + // embedded (and prepare to prepend it) + PPEmbedParameterPrefix &PrefixParam = *Params.MaybePrefixParam; + if (!TokenListIsCharacterArray(*this, TargetCharWidth, true, + PrefixParam.Tokens, BinaryPrefix)) { + HandleEmbedDirectiveNaive(HashLoc, FilenameTok.getLocation(), Params, + BinaryContents, TargetCharWidth); + return; } } if (Params.MaybeSuffixParam) { - std::copy(Params.MaybeSuffixParam->begin(), Params.MaybeSuffixParam->end(), - InitListTokens.get() + TokenIndex); - TokenIndex += Params.MaybeSuffixParam->size(); + // If we ahve a prefix, validate that it's a good fit for direct data + // embedding (and prepare to append it) + PPEmbedParameterSuffix &SuffixParam = *Params.MaybeSuffixParam; + if (!TokenListIsCharacterArray(*this, TargetCharWidth, false, + SuffixParam.Tokens, BinarySuffix)) { + HandleEmbedDirectiveNaive(HashLoc, FilenameTok.getLocation(), Params, + BinaryContents, TargetCharWidth); + return; + } } - assert(TokenIndex == InitListTokensSize); - EnterTokenStream(std::move(InitListTokens), InitListTokensSize, true, false); -} -void Preprocessor::HandleEmbedDirectiveBuiltin(SourceLocation FilenameLoc, - LexEmbedParametersResult &Params, - StringRef BinaryContents, - const size_t TargetCharWidth) { - // TODO: implement direct built-in support - HandleEmbedDirectiveNaive(FilenameLoc, Params, BinaryContents, - TargetCharWidth); + // Load up a new embed buffer for this file and set of parameters in + // particular. + EmbedBuffers.push_back(""); + size_t EmbedBufferNumber = EmbedBuffers.size(); + std::string EmbedBufferNumberVal = std::to_string(EmbedBufferNumber); + llvm::Twine EmbedBufferName = [](const std::string &Number) { + llvm::Twine PrefixNumber = (""); + }(EmbedBufferNumberVal); + std::string &TargetEmbedBuffer = EmbedBuffers.back(); + + // Generate the look-alike source file + TargetEmbedBuffer.append("__builtin_pp_embed(unsigned char,\""); + TargetEmbedBuffer.append(ResolvedFilename.data(), ResolvedFilename.size()); + TargetEmbedBuffer.append("\",\""); + // include the prefix(...) and suffix(...) binary data in the total contents + TripleEncodeBase64( + StringRef(BinaryPrefix.data(), BinaryPrefix.size()), BinaryContents, + StringRef(BinarySuffix.data(), BinarySuffix.size()), TargetEmbedBuffer); + TargetEmbedBuffer.append("\")"); + // Create faux-file and its ID, backed by a memory buffer. + std::unique_ptr EmbedMemBuffer = + llvm::MemoryBuffer::getMemBufferCopy(TargetEmbedBuffer, EmbedBufferName); + assert(EmbedMemBuffer && "Cannot create predefined source buffer"); + FileID EmbedBufferFID = SourceMgr.createFileID(std::move(EmbedMemBuffer)); + assert(EmbedBufferFID.isValid() && + "Could not create FileID for #embed directive?"); + // Start parsing the look-alike source file for the embed directive and + // pretend everything is normal + // TODO: (Maybe? )Stop the PPCallbacks from considering this a Real File™. + EnterSourceFile(EmbedBufferFID, nullptr, HashLoc, false); } void Preprocessor::HandleEmbedDirective(SourceLocation HashLoc, Token &EmbedTok, const FileEntry *LookupFromFile) { if (!LangOpts.C23 || !LangOpts.CPlusPlus26) { - auto EitherDiag = (LangOpts.CPlusPlus ? diag::warn_c23_pp_embed - : diag::warn_cxx26_pp_embed); + auto EitherDiag = (LangOpts.CPlusPlus ? diag::warn_cxx26_pp_embed + : diag::warn_c23_pp_embed); Diag(EmbedTok, EitherDiag); } @@ -3958,9 +4127,7 @@ void Preprocessor::HandleEmbedDirective(SourceLocation HashLoc, Token &EmbedTok, } std::optional MaybeSignedLimit{}; if (Params.MaybeLimitParam) { - if (static_cast(INT64_MAX) >= *Params.MaybeLimitParam) { - MaybeSignedLimit = static_cast(*Params.MaybeLimitParam); - } + MaybeSignedLimit = static_cast(Params.MaybeLimitParam->Limit); } llvm::ErrorOr> MaybeFile = getFileManager().getBufferForFile( *MaybeFileRef, false, false, MaybeSignedLimit); @@ -3973,7 +4140,7 @@ void Preprocessor::HandleEmbedDirective(SourceLocation HashLoc, Token &EmbedTok, StringRef BinaryContents = MaybeFile.get()->getBuffer(); if (Params.MaybeOffsetParam) { // offsets all the way to the end of the file make for an empty file. - const size_t OffsetParam = *Params.MaybeOffsetParam; + const size_t &OffsetParam = Params.MaybeOffsetParam->Offset; BinaryContents = BinaryContents.substr(OffsetParam); } const size_t TargetCharWidth = getTargetInfo().getCharWidth(); @@ -4009,11 +4176,12 @@ void Preprocessor::HandleEmbedDirective(SourceLocation HashLoc, Token &EmbedTok, RelativePath); } if (PPOpts->NoBuiltinPPEmbed) { - HandleEmbedDirectiveNaive(FilenameLoc, Params, BinaryContents, + HandleEmbedDirectiveNaive(HashLoc, FilenameLoc, Params, BinaryContents, TargetCharWidth); } else { // emit a token directly, handle it internally. - HandleEmbedDirectiveBuiltin(FilenameLoc, Params, BinaryContents, + HandleEmbedDirectiveBuiltin(HashLoc, FilenameTok, Filename, SearchPath, + RelativePath, Params, BinaryContents, TargetCharWidth); } } diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 6e0163ccc89b7f..5118cf5f3a7143 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1270,8 +1270,8 @@ static bool EvaluateHasIncludeCommon(Token &Tok, IdentifierInfo *II, int Preprocessor::EvaluateHasEmbed(Token &Tok, IdentifierInfo *II) { // pedwarn for not being on C23 if (!LangOpts.C23 || !LangOpts.CPlusPlus26) { - auto EitherDiag = (LangOpts.CPlusPlus ? diag::warn_c23_pp_has_embed - : diag::warn_cxx26_pp_has_embed); + auto EitherDiag = (LangOpts.CPlusPlus ? diag::warn_cxx26_pp_has_embed + : diag::warn_c23_pp_has_embed); Diag(Tok, EitherDiag); } @@ -1363,11 +1363,15 @@ int Preprocessor::EvaluateHasEmbed(Token &Tok, IdentifierInfo *II) { return VALUE__STDC_EMBED_NOT_FOUND__; } size_t FileSize = MaybeFileEntry->getSize(); - if (FileSize == 0 || - (Params.MaybeLimitParam ? *Params.MaybeLimitParam == 0 : false)) { + if (Params.MaybeLimitParam) { + if (FileSize > Params.MaybeLimitParam->Limit) { + FileSize = Params.MaybeLimitParam->Limit; + } + } + if (FileSize == 0) { return VALUE__STDC_EMBED_EMPTY__; } - if (Params.MaybeOffsetParam && *Params.MaybeOffsetParam >= FileSize) { + if (Params.MaybeOffsetParam && Params.MaybeOffsetParam->Offset >= FileSize) { return VALUE__STDC_EMBED_EMPTY__; } return VALUE__STDC_EMBED_FOUND__; diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index ede4c51487ffbe..bd12e71a28206c 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -1411,7 +1411,7 @@ bool Preprocessor::FinishLexStringLiteral(Token &Result, std::string &String, return true; } -bool Preprocessor::parseSimpleIntegerLiteral(Token &Tok, uint64_t &Value) { +bool Preprocessor::parseSimpleIntegerLiteral(Token &Tok, uint64_t &Value, bool WithLex) { assert(Tok.is(tok::numeric_constant)); SmallString<8> IntegerBuffer; bool NumberInvalid = false; @@ -1426,7 +1426,8 @@ bool Preprocessor::parseSimpleIntegerLiteral(Token &Tok, uint64_t &Value) { llvm::APInt APVal(64, 0); if (Literal.GetIntegerValue(APVal)) return false; - Lex(Tok); + if (WithLex) + Lex(Tok); Value = APVal.getLimitedValue(); return true; } diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 9dbfc1c8c5e9ff..ff8ee15e9bfb4e 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -32,6 +32,7 @@ #include "clang/Sema/Scope.h" #include "clang/Sema/TypoCorrection.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Base64.h" #include using namespace clang; @@ -805,6 +806,7 @@ class CastExpressionIdValidator final : public CorrectionCandidateCallback { /// [MS] '__builtin_FUNCSIG' '(' ')' /// [GNU] '__builtin_LINE' '(' ')' /// [CLANG] '__builtin_COLUMN' '(' ')' +/// [CLANG] '__builtin_pp_embed' '(' type-name ',' string-literal ',' string-literal ')' /// [GNU] '__builtin_source_location' '(' ')' /// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')' /// [GNU] '__null' @@ -1345,6 +1347,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::kw___builtin_FUNCSIG: case tok::kw___builtin_LINE: case tok::kw___builtin_source_location: + case tok::kw___builtin_pp_embed: if (NotPrimaryExpression) *NotPrimaryExpression = true; // This parses the complete suffix; we can return early. @@ -2145,6 +2148,7 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { } else { Expr *Fn = LHS.get(); SourceLocation RParLoc = Tok.getLocation(); + Actions.ModifyCallExprArguments(Fn, Loc, ArgExprs, RParLoc); LHS = Actions.ActOnCallExpr(getCurScope(), Fn, Loc, ArgExprs, RParLoc, ExecConfig); if (LHS.isInvalid()) { @@ -2575,6 +2579,7 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { /// [MS] '__builtin_FUNCSIG' '(' ')' /// [GNU] '__builtin_LINE' '(' ')' /// [CLANG] '__builtin_COLUMN' '(' ')' +/// [CLANG] '__builtin_pp_embed' '(' 'type-name ',' string-literal ',' string-literal ')' /// [GNU] '__builtin_source_location' '(' ')' /// [OCL] '__builtin_astype' '(' assignment-expression ',' type-name ')' /// @@ -2841,6 +2846,96 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() { Res = Actions.ActOnSourceLocExpr(Kind, StartLoc, ConsumeParen()); break; } + case tok::kw___builtin_pp_embed: { + SourceRange DataTyExprSourceRange{}; + TypeResult DataTyExpr(ParseTypeName(&DataTyExprSourceRange)); + + if (ExpectAndConsume(tok::comma)) { + SkipUntil(tok::r_paren, StopAtSemi); + Res = ExprError(); + } + + ExprResult FilenameArgExpr(ParseStringLiteralExpression()); + + if (ExpectAndConsume(tok::comma)) { + SkipUntil(tok::r_paren, StopAtSemi); + Res = ExprError(); + } + + ExprResult Base64ArgExpr(ParseStringLiteralExpression()); + + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected) << tok::r_paren; + Res = ExprError(); + } + + const ASTContext &Context = Actions.getASTContext(); + QualType DataTy = Context.UnsignedCharTy; + size_t TargetWidth = Context.getTypeSize(DataTy); + if (DataTyExpr.isInvalid()) { + Res = ExprError(); + } else { + DataTy = DataTyExpr.get().get().getCanonicalType(); + TargetWidth = Context.getTypeSize(DataTy); + if (DataTy.getUnqualifiedType() != Context.UnsignedCharTy && + DataTy.getUnqualifiedType() != Context.CharTy) { + // TODO: check if is exactly the same as unsigned char + Diag(DataTyExprSourceRange.getBegin(), + diag::err_builtin_pp_embed_invalid_argument) + << "only 'char' and 'unsigned char' are supported"; + Res = ExprError(); + } + if ((TargetWidth % CHAR_BIT) != 0) { + Diag(DataTyExprSourceRange.getBegin(), + diag::err_builtin_pp_embed_invalid_argument) + << "width of element type is not a multiple of host platform's " + "CHAR_BIT!"; + Res = ExprError(); + } + } + + StringLiteral *FilenameLiteral = nullptr; + if (FilenameArgExpr.isInvalid()) { + Res = ExprError(); + } else { + FilenameLiteral = FilenameArgExpr.getAs(); + } + + std::vector BinaryData{}; + if (Base64ArgExpr.isInvalid()) { + Res = ExprError(); + } else { + StringLiteral *Base64Str = Base64ArgExpr.getAs(); + StringRef Base64StrData = Base64Str->getBytes(); + if (Base64Str->getKind() != StringLiteral::Ordinary) { + Diag(Base64Str->getExprLoc(), diag::err_expected_string_literal) + << 0 + << "'__builtin_pp_embed' with valid base64 encoding that is an " + "ordinary \"...\" string"; + } + const auto OnDecodeError = [&](const llvm::ErrorInfoBase &) { + Diag(Base64Str->getExprLoc(), + diag::err_builtin_pp_embed_invalid_argument) + << "expected a valid base64 encoded string"; + }; + llvm::Error Err = llvm::decodeBase64(Base64Str->getBytes(), BinaryData); + llvm::handleAllErrors(std::move(Err), OnDecodeError); + if (((BinaryData.size() * CHAR_BIT) % TargetWidth) != 0) { + Diag(DataTyExprSourceRange.getBegin(), + diag::err_builtin_pp_embed_invalid_argument) + << "size of data does not split evently into the number of bytes " + "requested"; + Res = ExprError(); + } + } + + if (!Res.isInvalid()) { + Res = Actions.ActOnPPEmbedExpr( + StartLoc, Base64ArgExpr.get()->getExprLoc(), ConsumeParen(), + FilenameLiteral, DataTy, std::move(BinaryData)); + } + break; + } } if (Res.isInvalid()) diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index f556d0e6d4f8b6..8364519861fe4f 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -1671,6 +1671,8 @@ bool Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs, // arguments. } while (TryConsumeToken(tok::comma)); + Actions.ModifyTemplateArguments(Template, TemplateArgs); + return false; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 23b743d67a16b0..97ac254242defe 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13336,6 +13336,53 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { return; } + // Adjust the init expression for PPEmbedExpr as early as possible + // here. + bool AlreadyAdjustedPPEmbedExpr = false; + if (InitListExpr *ILExpr = dyn_cast_if_present(Init); ILExpr) { + QualType VDeclTy = VDecl->getType(); + ArrayRef Inits = ILExpr->inits(); + if (CheckExprListForPPEmbedExpr(Inits, VDeclTy) == PPEmbedExpr::FoundOne) { + PPEmbedExpr *PPEmbed = dyn_cast_if_present(Inits[0]); + ILExpr->setInit(0, PPEmbed->getDataStringLiteral()); + AlreadyAdjustedPPEmbedExpr = true; + } + } + + if (!AlreadyAdjustedPPEmbedExpr) { + // If there is a PPEmbedExpr as a single initializer without braces, + // make sure it only produces a single element (and then expand said + // element). + if (PPEmbedExpr *PPEmbed = dyn_cast_if_present(Init); + PPEmbed) { + if (PPEmbed->getDataElementCount(Context) == 1) { + // Expand the list in-place immediately, let the natural work take hold + Init = ExpandSinglePPEmbedExpr(PPEmbed); + } else { + // `__builtin_pp_embed( ... )` only produces 2 or more values. + Diag(RealDecl->getLocation(), diag::err_illegal_initializer_type) + << "'__builtin_pp_embed'"; + RealDecl->setInvalidDecl(); + return; + } + } + + // Legitimately, in all other cases, COMPLETELY nuke the PPEmbedExpr + // and turn it into a list of integers where applicable. + if (InitListExpr *ILExpr = dyn_cast_if_present(Init); ILExpr) { + ArrayRef Inits = ILExpr->inits(); + SmallVector OutputExprList{}; + if (ExpandPPEmbedExprInExprList(Inits, OutputExprList, false) == + PPEmbedExpr::Expanded) { + ILExpr->resizeInits(Context, OutputExprList.size()); + for (size_t I = 0; I < OutputExprList.size(); ++I) { + auto &InitExpr = OutputExprList[I]; + ILExpr->setInit(I, InitExpr); + } + } + } + } + // WebAssembly tables can't be used to initialise a variable. if (Init && !Init->getType().isNull() && Init->getType()->isWebAssemblyTableType()) { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index f9c010b1a00248..25355c00b2ae94 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17022,7 +17022,8 @@ Decl *Sema::ActOnStaticAssertDeclaration(SourceLocation StaticAssertLoc, SourceLocation RParenLoc) { if (DiagnoseUnexpandedParameterPack(AssertExpr, UPPC_StaticAssertExpression)) return nullptr; - + if (DiagnosePPEmbedExpr(AssertExpr, StaticAssertLoc, PPEEC_StaticAssert)) + return nullptr; return BuildStaticAssertDeclaration(StaticAssertLoc, AssertExpr, AssertMessageExpr, RParenLoc, false); } diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 75730ea888afb4..ebeed7f4d2b485 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1412,6 +1412,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::SizeOfPackExprClass: case Expr::StringLiteralClass: case Expr::SourceLocExprClass: + case Expr::PPEmbedExprClass: case Expr::ConceptSpecializationExprClass: case Expr::RequiresExprClass: // These expressions can never throw. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 9c5f96eebd0416..80d4b322a62cee 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7110,6 +7110,13 @@ static void DiagnosedUnqualifiedCallsToStdFunctions(Sema &S, << FixItHint::CreateInsertion(DRE->getLocation(), "std::"); } +void Sema::ModifyCallExprArguments(Expr *Fn, SourceLocation LParenLoc, + SmallVectorImpl &ArgExprs, + SourceLocation RParenLoc) { + [[maybe_unused]] PPEmbedExpr::Action Action = + ExpandPPEmbedExprInExprList(ArgExprs); +} + ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, MultiExprArg ArgExprs, SourceLocation RParenLoc, Expr *ExecConfig) { @@ -7947,8 +7954,17 @@ Sema::BuildInitList(SourceLocation LBraceLoc, MultiExprArg InitArgList, } } - InitListExpr *E = new (Context) InitListExpr(Context, LBraceLoc, InitArgList, - RBraceLoc); + InitListExpr *E = nullptr; + if (InitArgList.size() > 1 && + CheckExprListForPPEmbedExpr(InitArgList, std::nullopt) != + PPEmbedExpr::NotFound) { + SmallVector OutputExprList; + ExpandPPEmbedExprInExprList(InitArgList, OutputExprList); + E = new (Context) + InitListExpr(Context, LBraceLoc, OutputExprList, RBraceLoc); + } else { + E = new (Context) InitListExpr(Context, LBraceLoc, InitArgList, RBraceLoc); + } E->setType(Context.VoidTy); // FIXME: just a place holder for now. return E; } @@ -17571,6 +17587,224 @@ ExprResult Sema::BuildSourceLocExpr(SourceLocExpr::IdentKind Kind, SourceLocExpr(Context, Kind, ResultTy, BuiltinLoc, RPLoc, ParentContext); } +ExprResult Sema::ActOnPPEmbedExpr(SourceLocation BuiltinLoc, + SourceLocation Base64DataLocation, + SourceLocation RPLoc, StringLiteral *Filename, + QualType ElementTy, + std::vector BinaryData) { + uint64_t ArraySizeRawVal[] = {BinaryData.size()}; + llvm::APSInt ArraySize(llvm::APInt(Context.getTypeSize(Context.getSizeType()), + 1, ArraySizeRawVal)); + QualType ArrayTy = Context.getConstantArrayType(ElementTy, ArraySize, nullptr, + ArrayType::Normal, 0); + StringLiteral *BinaryDataLiteral = StringLiteral::Create( + Context, StringRef(BinaryData.data(), BinaryData.size()), + StringLiteral::Ordinary, false, ArrayTy, Base64DataLocation); + return new (Context) + PPEmbedExpr(Context, ElementTy, Filename, BinaryDataLiteral, BuiltinLoc, + RPLoc, CurContext); +} + +IntegerLiteral *Sema::ExpandSinglePPEmbedExpr(PPEmbedExpr *PPEmbed) { + assert(PPEmbed->getDataElementCount(Context) == 1 && + "Data should only contain a single element"); + StringLiteral *DataLiteral = PPEmbed->getDataStringLiteral(); + QualType ElementTy = PPEmbed->getType(); + const size_t TargetWidth = Context.getTypeSize(ElementTy); + const size_t BytesPerElement = CHAR_BIT / TargetWidth; + StringRef Data = DataLiteral->getBytes(); + SmallVector ByteVals{}; + for (size_t ValIndex = 0; ValIndex < BytesPerElement; ++ValIndex) { + if ((ValIndex % sizeof(uint64_t)) == 0) { + ByteVals.push_back(0); + } + const unsigned char DataByte = Data[ValIndex]; + ByteVals.back() |= + (static_cast(DataByte) << (ValIndex * CHAR_BIT)); + } + ArrayRef ByteValsRef(ByteVals); + return IntegerLiteral::Create(Context, llvm::APInt(TargetWidth, ByteValsRef), + ElementTy, DataLiteral->getBeginLoc()); +} + +PPEmbedExpr::Action +Sema::CheckExprListForPPEmbedExpr(ArrayRef ExprList, + std::optional MaybeInitType) { + if (ExprList.empty()) { + return PPEmbedExpr::NotFound; + } + PPEmbedExpr *First = ExprList.size() == 1 + ? dyn_cast_if_present(ExprList[0]) + : nullptr; + if (First) { + // only one and it's an embed + if (MaybeInitType) { + // With the type information, we have a duty to check if it matches; + // if not, explode it out into a list of integer literals. + QualType &InitType = *MaybeInitType; + if (InitType->isArrayType()) { + const ArrayType *InitArrayType = InitType->getAsArrayTypeUnsafe(); + QualType InitElementTy = InitArrayType->getElementType(); + QualType PPEmbedExprElementTy = First->getType(); + const bool TypesMatch = + Context.typesAreCompatible(InitElementTy, PPEmbedExprElementTy) || + (InitElementTy->isCharType() && PPEmbedExprElementTy->isCharType()); + if (TypesMatch) { + // Keep the PPEmbedExpr, report that everything has been found. + return PPEmbedExpr::FoundOne; + } + } + } else { + // leave it, possibly adjusted later! + return PPEmbedExpr::FoundOne; + } + } + if (std::find_if(ExprList.begin(), ExprList.end(), + [](const Expr *const SomeExpr) { + return isa(SomeExpr); + }) == ExprList.end()) { + // We didn't find one. + return PPEmbedExpr::NotFound; + } + // Otherwise, we found one but it is not the sole entry in the initialization + // list. + return PPEmbedExpr::Expanded; +} + +PPEmbedExpr::Action +Sema::ExpandPPEmbedExprInExprList(SmallVectorImpl &ExprList) { + PPEmbedExpr::Action Action = PPEmbedExpr::NotFound; + SmallVector ByteVals{}; + for (size_t I = 0; I < ExprList.size();) { + Expr *&OriginalExpr = ExprList[I]; + PPEmbedExpr *PPEmbed = dyn_cast_if_present(OriginalExpr); + if (!PPEmbed) { + ++I; + continue; + } + auto ExprListIt = ExprList.erase(&OriginalExpr); + const size_t ExpectedDataElements = PPEmbed->getDataElementCount(Context); + if (ExpectedDataElements == 0) { + // No ++I, we are already pointing to newest element. + continue; + } + Action = PPEmbedExpr::Expanded; + StringLiteral *DataLiteral = PPEmbed->getDataStringLiteral(); + QualType ElementTy = PPEmbed->getType(); + const size_t TargetWidth = Context.getTypeSize(ElementTy); + const size_t BytesPerElement = CHAR_BIT / TargetWidth; + StringRef Data = DataLiteral->getBytes(); + size_t Insertions = 0; + for (size_t ByteIndex = 0; ByteIndex < Data.size(); + ByteIndex += BytesPerElement) { + ByteVals.clear(); + for (size_t ValIndex = 0; ValIndex < BytesPerElement; ++ValIndex) { + if ((ValIndex % sizeof(uint64_t)) == 0) { + ByteVals.push_back(0); + } + const unsigned char DataByte = Data[ByteIndex + ValIndex]; + ByteVals.back() |= + (static_cast(DataByte) << (ValIndex * CHAR_BIT)); + } + ArrayRef ByteValsRef(ByteVals); + IntegerLiteral *IntLit = + IntegerLiteral::Create(Context, llvm::APInt(TargetWidth, ByteValsRef), + ElementTy, DataLiteral->getBeginLoc()); + ExprListIt = ExprList.insert(ExprListIt, IntLit); + ++Insertions; + // make sure we are inserting **after** the item we just inserted, not + // before + ++ExprListIt; + } + assert(Insertions == ExpectedDataElements); + I += Insertions; + } + return PPEmbedExpr::Expanded; +} + +PPEmbedExpr::Action +Sema::ExpandPPEmbedExprInExprList(ArrayRef ExprList, + SmallVectorImpl &OutputExprList, + bool ClearOutputFirst) { + if (ClearOutputFirst) { + OutputExprList.clear(); + } + size_t ExpectedResize = OutputExprList.size() + ExprList.size(); + const auto FindPPEmbedExpr = [](const Expr *const SomeExpr) { + return isa(SomeExpr); + }; + if (std::find_if(ExprList.begin(), ExprList.end(), FindPPEmbedExpr) == + ExprList.end()) { + return PPEmbedExpr::NotFound; + } + SmallVector ByteVals{}; + OutputExprList.reserve(ExpectedResize); + for (size_t I = 0; I < ExprList.size(); ++I) { + Expr *OriginalExpr = ExprList[I]; + PPEmbedExpr *PPEmbed = dyn_cast_if_present(OriginalExpr); + if (!PPEmbed) { + OutputExprList.push_back(OriginalExpr); + continue; + } + StringLiteral *DataLiteral = PPEmbed->getDataStringLiteral(); + QualType ElementTy = PPEmbed->getType(); + const size_t TargetWidth = Context.getTypeSize(ElementTy); + const size_t BytesPerElement = CHAR_BIT / TargetWidth; + StringRef Data = DataLiteral->getBytes(); + for (size_t ByteIndex = 0; ByteIndex < Data.size(); + ByteIndex += BytesPerElement) { + ByteVals.clear(); + for (size_t ValIndex = 0; ValIndex < BytesPerElement; ++ValIndex) { + if ((ValIndex % sizeof(uint64_t)) == 0) { + ByteVals.push_back(0); + } + const unsigned char DataByte = Data[ByteIndex + ValIndex]; + ByteVals.back() |= + (static_cast(DataByte) << (ValIndex * CHAR_BIT)); + } + ArrayRef ByteValsRef(ByteVals); + IntegerLiteral *IntLit = + IntegerLiteral::Create(Context, llvm::APInt(TargetWidth, ByteValsRef), + ElementTy, DataLiteral->getBeginLoc()); + OutputExprList.push_back(IntLit); + } + } + return PPEmbedExpr::Expanded; +} + +StringRef Sema::GetLocationName(PPEmbedExprContext Context) const { + switch (Context) { + default: + llvm_unreachable("unhandled PPEmbedExprContext value"); + case PPEEC__StaticAssert: + return "_Static_assert"; + case PPEEC_StaticAssert: + return "static_assert"; + } +} + +bool Sema::DiagnosePPEmbedExpr(Expr *&E, SourceLocation ContextLocation, + PPEmbedExprContext PPEmbedContext, + bool SingleAllowed) { + PPEmbedExpr *PPEmbed = dyn_cast_if_present(E); + if (!PPEmbed) + return true; + + if (SingleAllowed && PPEmbed->getDataElementCount(Context) == 1) { + E = ExpandSinglePPEmbedExpr(PPEmbed); + return true; + } + + StringRef LocationName = GetLocationName(PPEmbedContext); + StringRef DiagnosticMessage = (SingleAllowed + ? "cannot use a preprocessor embed that expands to nothing or expands to " + "more than one item in " + : "cannot use a preprocessor embed in "); + Diag(ContextLocation, diag::err_builtin_pp_embed_invalid_location) + << DiagnosticMessage << 1 << LocationName; + return false; +} + bool Sema::CheckConversionToObjCLiteral(QualType DstType, Expr *&Exp, bool Diagnose) { if (!getLangOpts().ObjC) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index ff370dd1e080b2..234e678c71b140 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1623,6 +1623,62 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, return Param; } +void Sema::ModifyTemplateArguments( + const TemplateTy &Template, + SmallVectorImpl &TemplateArgs) { + SmallVector ByteVals{}; + for (size_t I = 0; I < TemplateArgs.size();) { + ParsedTemplateArgument &OriginalArg = TemplateArgs[I]; + if (OriginalArg.getKind() != ParsedTemplateArgument::NonType) { + ++I; + continue; + } + PPEmbedExpr *PPEmbed = dyn_cast(OriginalArg.getAsExpr()); + if (!PPEmbed) { + ++I; + continue; + } + auto TemplateArgListIt = TemplateArgs.erase(&OriginalArg); + const size_t ExpectedDataElements = PPEmbed->getDataElementCount(Context); + if (ExpectedDataElements == 0) { + // No ++I; already pointing at the right element! + continue; + } + StringLiteral *DataLiteral = PPEmbed->getDataStringLiteral(); + QualType ElementTy = PPEmbed->getType(); + const size_t TargetWidth = Context.getTypeSize(ElementTy); + const size_t BytesPerElement = CHAR_BIT / TargetWidth; + StringRef Data = DataLiteral->getBytes(); + size_t Insertions = 0; + for (size_t ByteIndex = 0; ByteIndex < Data.size(); + ByteIndex += BytesPerElement) { + ByteVals.clear(); + for (size_t ValIndex = 0; ValIndex < BytesPerElement; ++ValIndex) { + if ((ValIndex % sizeof(uint64_t)) == 0) { + ByteVals.push_back(0); + } + const unsigned char DataByte = Data[ByteIndex + ValIndex]; + ByteVals.back() |= + (static_cast(DataByte) << (ValIndex * CHAR_BIT)); + } + ArrayRef ByteValsRef(ByteVals); + IntegerLiteral *IntLit = + IntegerLiteral::Create(Context, llvm::APInt(TargetWidth, ByteValsRef), + ElementTy, DataLiteral->getBeginLoc()); + TemplateArgListIt = TemplateArgs.insert( + TemplateArgListIt, + ParsedTemplateArgument(ParsedTemplateArgument::NonType, IntLit, + OriginalArg.getLocation())); + ++Insertions; + // make sure we are inserting **after** the item we just inserted, not + // before + ++TemplateArgListIt; + } + assert(Insertions == ExpectedDataElements); + I += Insertions; + } +} + /// ActOnTemplateTemplateParameter - Called when a C++ template template /// parameter (e.g. T in template