diff options
Diffstat (limited to 'clang/lib')
34 files changed, 1303 insertions, 98 deletions
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 7e55568..04b331a 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2373,6 +2373,17 @@ APValue SourceLocExpr::EvaluateInContext(const ASTContext &Ctx, llvm_unreachable("unhandled case"); } +EmbedExpr::EmbedExpr(const ASTContext &Ctx, SourceLocation Loc, + EmbedDataStorage *Data, unsigned Begin, + unsigned NumOfElements) + : Expr(EmbedExprClass, Ctx.UnsignedCharTy, VK_PRValue, OK_Ordinary), + EmbedKeywordLoc(Loc), Ctx(&Ctx), Data(Data), Begin(Begin), + NumOfElements(NumOfElements) { + setDependence(ExprDependence::None); + FakeChildNode = IntegerLiteral::Create( + Ctx, llvm::APInt::getZero(Ctx.getTypeSize(getType())), getType(), Loc); +} + InitListExpr::InitListExpr(const ASTContext &C, SourceLocation lbraceloc, ArrayRef<Expr *> initExprs, SourceLocation rbraceloc) : Expr(InitListExprClass, QualType(), VK_PRValue, OK_Ordinary), @@ -3615,6 +3626,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case CXXUuidofExprClass: case OpaqueValueExprClass: case SourceLocExprClass: + case EmbedExprClass: case ConceptSpecializationExprClass: case RequiresExprClass: case SYCLUniqueStableNameExprClass: diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 390000e3..6482cb6 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -204,6 +204,11 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::RequiresExprClass: return Cl::CL_PRValue; + case Expr::EmbedExprClass: + // 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 d505745..af1f18a 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -7727,6 +7727,11 @@ public: return Error(E); } + bool VisitEmbedExpr(const EmbedExpr *E) { + const auto It = E->begin(); + return StmtVisitorTy::Visit(*It); + } + bool VisitPredefinedExpr(const PredefinedExpr *E) { return StmtVisitorTy::Visit(E->getFunctionName()); } @@ -9145,6 +9150,11 @@ public: return true; } + bool VisitEmbedExpr(const EmbedExpr *E) { + llvm_unreachable("Not yet implemented for ExprConstant.cpp"); + return true; + } + bool VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E) { std::string ResultStr = E->ComputeName(Info.Ctx); @@ -11249,8 +11259,17 @@ bool ArrayExprEvaluator::VisitCXXParenListOrInitListExpr( // If the initializer might depend on the array index, run it for each // array element. - if (NumEltsToInit != NumElts && MaybeElementDependentArrayFiller(ArrayFiller)) + if (NumEltsToInit != NumElts && + MaybeElementDependentArrayFiller(ArrayFiller)) { NumEltsToInit = NumElts; + } else { + for (auto *Init : Args) { + if (auto *EmbedS = dyn_cast<EmbedExpr>(Init->IgnoreParenImpCasts())) + NumEltsToInit += EmbedS->getDataElementCount() - 1; + } + if (NumEltsToInit > NumElts) + NumEltsToInit = NumElts; + } LLVM_DEBUG(llvm::dbgs() << "The number of elements to initialize: " << NumEltsToInit << ".\n"); @@ -11268,16 +11287,49 @@ bool ArrayExprEvaluator::VisitCXXParenListOrInitListExpr( LValue Subobject = This; Subobject.addArray(Info, ExprToVisit, CAT); - for (unsigned Index = 0; Index != NumEltsToInit; ++Index) { - const Expr *Init = Index < Args.size() ? Args[Index] : ArrayFiller; - if (!EvaluateInPlace(Result.getArrayInitializedElt(Index), - Info, Subobject, Init) || + auto Eval = [&](const Expr *Init, unsigned ArrayIndex) { + if (!EvaluateInPlace(Result.getArrayInitializedElt(ArrayIndex), Info, + Subobject, Init) || !HandleLValueArrayAdjustment(Info, Init, Subobject, CAT->getElementType(), 1)) { if (!Info.noteFailure()) return false; Success = false; } + return true; + }; + unsigned ArrayIndex = 0; + QualType DestTy = CAT->getElementType(); + APSInt Value(Info.Ctx.getTypeSize(DestTy), DestTy->isUnsignedIntegerType()); + for (unsigned Index = 0; Index != NumEltsToInit; ++Index) { + const Expr *Init = Index < Args.size() ? Args[Index] : ArrayFiller; + if (ArrayIndex >= NumEltsToInit) + break; + if (auto *EmbedS = dyn_cast<EmbedExpr>(Init->IgnoreParenImpCasts())) { + StringLiteral *SL = EmbedS->getDataStringLiteral(); + for (unsigned I = EmbedS->getStartingElementPos(), + N = EmbedS->getDataElementCount(); + I != EmbedS->getStartingElementPos() + N; ++I) { + Value = SL->getCodeUnit(I); + if (DestTy->isIntegerType()) { + Result.getArrayInitializedElt(ArrayIndex) = APValue(Value); + } else { + assert(DestTy->isFloatingType() && "unexpected type"); + const FPOptions FPO = + Init->getFPFeaturesInEffect(Info.Ctx.getLangOpts()); + APFloat FValue(0.0); + if (!HandleIntToFloatCast(Info, Init, FPO, EmbedS->getType(), Value, + DestTy, FValue)) + return false; + Result.getArrayInitializedElt(ArrayIndex) = APValue(FValue); + } + ArrayIndex++; + } + } else { + if (!Eval(Init, ArrayIndex)) + return false; + ++ArrayIndex; + } } if (!Result.hasArrayFiller()) @@ -16363,6 +16415,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::SizeOfPackExprClass: case Expr::GNUNullExprClass: case Expr::SourceLocExprClass: + case Expr::EmbedExprClass: return NoDiag(); case Expr::PackIndexingExprClass: diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 1393ef1..e766558 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1201,11 +1201,19 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits, } if (T->isArrayType()) { + auto Eval = [&](Expr *Init, unsigned ElemIndex) { + return visitArrayElemInit(ElemIndex, Init); + }; unsigned ElementIndex = 0; for (const Expr *Init : Inits) { - if (!this->visitArrayElemInit(ElementIndex, Init)) - return false; - ++ElementIndex; + if (auto *EmbedS = dyn_cast<EmbedExpr>(Init->IgnoreParenImpCasts())) { + if (!EmbedS->doForEachDataElement(Eval, ElementIndex)) + return false; + } else { + if (!this->visitArrayElemInit(ElementIndex, Init)) + return false; + ++ElementIndex; + } } // Expand the filler expression. @@ -1351,6 +1359,12 @@ bool ByteCodeExprGen<Emitter>::VisitConstantExpr(const ConstantExpr *E) { return this->delegate(E->getSubExpr()); } +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitEmbedExpr(const EmbedExpr *E) { + auto It = E->begin(); + return this->visit(*It); +} + static CharUnits AlignOfType(QualType T, const ASTContext &ASTCtx, UnaryExprOrTypeTrait Kind) { bool AlignOfReturnsPreferred = diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index 295cfef..f9f508e 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -115,6 +115,7 @@ public: bool VisitSizeOfPackExpr(const SizeOfPackExpr *E); bool VisitGenericSelectionExpr(const GenericSelectionExpr *E); bool VisitChooseExpr(const ChooseExpr *E); + bool VisitEmbedExpr(const EmbedExpr *E); bool VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *E); bool VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E); bool VisitExpressionTraitExpr(const ExpressionTraitExpr *E); diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index ed9e6eeb..eac1801 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4760,6 +4760,7 @@ recurse: case Expr::PseudoObjectExprClass: case Expr::AtomicExprClass: case Expr::SourceLocExprClass: + case Expr::EmbedExprClass: case Expr::BuiltinBitCastExprClass: { NotPrimaryExpr(); diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 8f51d16..2d223a9 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1177,6 +1177,10 @@ void StmtPrinter::VisitSourceLocExpr(SourceLocExpr *Node) { OS << Node->getBuiltinStr() << "()"; } +void StmtPrinter::VisitEmbedExpr(EmbedExpr *Node) { + assert(false && "not yet implemented"); +} + void StmtPrinter::VisitConstantExpr(ConstantExpr *Node) { PrintExpr(Node->getSubExpr()); } diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index d165590..1add5ca 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2313,6 +2313,8 @@ void StmtProfiler::VisitSourceLocExpr(const SourceLocExpr *E) { VisitExpr(E); } +void StmtProfiler::VisitEmbedExpr(const EmbedExpr *E) { VisitExpr(E); } + void StmtProfiler::VisitRecoveryExpr(const RecoveryExpr *E) { VisitExpr(E); } void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) { diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 1076dcd..e1a2709 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -2884,3 +2884,8 @@ void TextNodeDumper::VisitOpenACCLoopConstruct(const OpenACCLoopConstruct *S) { else OS << " parent: " << S->getParentComputeConstruct(); } + +void TextNodeDumper::VisitEmbedExpr(const EmbedExpr *S) { + AddChild("begin", [=] { OS << S->getStartingElementPos(); }); + AddChild("number of elements", [=] { OS << S->getDataElementCount(); }); +} diff --git a/clang/lib/Basic/FileManager.cpp b/clang/lib/Basic/FileManager.cpp index 1dc51de..4509cee 100644 --- a/clang/lib/Basic/FileManager.cpp +++ b/clang/lib/Basic/FileManager.cpp @@ -530,13 +530,18 @@ void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) { llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileManager::getBufferForFile(FileEntryRef FE, bool isVolatile, - bool RequiresNullTerminator) { + bool RequiresNullTerminator, + std::optional<int64_t> MaybeLimit) { const FileEntry *Entry = &FE.getFileEntry(); // If the content is living on the file entry, return a reference to it. if (Entry->Content) return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef()); uint64_t FileSize = Entry->getSize(); + + if (MaybeLimit) + FileSize = *MaybeLimit; + // If there's a high enough chance that the file have changed since we // got its size, force a stat before opening it. if (isVolatile || Entry->isNamedPipe()) diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp index feea845..04cc9c7 100644 --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -425,8 +425,8 @@ tok::PPKeywordKind IdentifierInfo::getPPKeywordID() const { // collisions (if there were, the switch below would complain about duplicate // case values). Note that this depends on 'if' being null terminated. -#define HASH(LEN, FIRST, THIRD) \ - (LEN << 5) + (((FIRST-'a') + (THIRD-'a')) & 31) +#define HASH(LEN, FIRST, THIRD) \ + (LEN << 6) + (((FIRST - 'a') - (THIRD - 'a')) & 63) #define CASE(LEN, FIRST, THIRD, NAME) \ case HASH(LEN, FIRST, THIRD): \ return memcmp(Name, #NAME, LEN) ? tok::pp_not_keyword : tok::pp_ ## NAME @@ -441,6 +441,7 @@ tok::PPKeywordKind IdentifierInfo::getPPKeywordID() const { CASE( 4, 'e', 's', else); CASE( 4, 'l', 'n', line); CASE( 4, 's', 'c', sccs); + CASE(5, 'e', 'b', embed); CASE( 5, 'e', 'd', endif); CASE( 5, 'e', 'r', error); CASE( 5, 'i', 'e', ident); diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index b2a5cee..a8bb254 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -509,6 +509,16 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, uint64_t NumInitElements = Args.size(); uint64_t NumArrayElements = AType->getNumElements(); + for (const auto *Init : Args) { + if (const auto *Embed = dyn_cast<EmbedExpr>(Init->IgnoreParenImpCasts())) { + NumInitElements += Embed->getDataElementCount() - 1; + if (NumInitElements > NumArrayElements) { + NumInitElements = NumArrayElements; + break; + } + } + } + assert(NumInitElements <= NumArrayElements); QualType elementType = @@ -577,23 +587,37 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, llvm::Value *one = llvm::ConstantInt::get(CGF.SizeTy, 1); - // Emit the explicit initializers. - for (uint64_t i = 0; i != NumInitElements; ++i) { + auto Emit = [&](Expr *Init, uint64_t ArrayIndex) { llvm::Value *element = begin; - if (i > 0) { - element = Builder.CreateInBoundsGEP(llvmElementType, begin, - llvm::ConstantInt::get(CGF.SizeTy, i), - "arrayinit.element"); + if (ArrayIndex > 0) { + element = Builder.CreateInBoundsGEP( + llvmElementType, begin, + llvm::ConstantInt::get(CGF.SizeTy, ArrayIndex), "arrayinit.element"); // Tell the cleanup that it needs to destroy up to this // element. TODO: some of these stores can be trivially // observed to be unnecessary. - if (endOfInit.isValid()) Builder.CreateStore(element, endOfInit); + if (endOfInit.isValid()) + Builder.CreateStore(element, endOfInit); } LValue elementLV = CGF.MakeAddrLValue( Address(element, llvmElementType, elementAlign), elementType); - EmitInitializationToLValue(Args[i], elementLV); + EmitInitializationToLValue(Init, elementLV); + return true; + }; + + unsigned ArrayIndex = 0; + // Emit the explicit initializers. + for (uint64_t i = 0; i != NumInitElements; ++i) { + if (ArrayIndex >= NumInitElements) + break; + if (auto *EmbedS = dyn_cast<EmbedExpr>(Args[i]->IgnoreParenImpCasts())) { + EmbedS->doForEachDataElement(Emit, ArrayIndex); + } else { + Emit(Args[i], ArrayIndex); + ArrayIndex++; + } } // Check whether there's a non-trivial array-fill expression. diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 0712f40..0fd3792 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1061,6 +1061,24 @@ public: return Visit(E->getInitializer(), T); } + llvm::Constant *ProduceIntToIntCast(const Expr *E, QualType DestType) { + QualType FromType = E->getType(); + // See also HandleIntToIntCast in ExprConstant.cpp + if (FromType->isIntegerType()) + if (llvm::Constant *C = Visit(E, FromType)) + if (auto *CI = dyn_cast<llvm::ConstantInt>(C)) { + unsigned SrcWidth = CGM.getContext().getIntWidth(FromType); + unsigned DstWidth = CGM.getContext().getIntWidth(DestType); + if (DstWidth == SrcWidth) + return CI; + llvm::APInt A = FromType->isSignedIntegerType() + ? CI->getValue().sextOrTrunc(DstWidth) + : CI->getValue().zextOrTrunc(DstWidth); + return llvm::ConstantInt::get(CGM.getLLVMContext(), A); + } + return nullptr; + } + llvm::Constant *VisitCastExpr(const CastExpr *E, QualType destType) { if (const auto *ECE = dyn_cast<ExplicitCastExpr>(E)) CGM.EmitExplicitCastExprType(ECE, Emitter.CGF); @@ -1142,23 +1160,8 @@ public: case CK_IntToOCLSampler: llvm_unreachable("global sampler variables are not generated"); - case CK_IntegralCast: { - QualType FromType = subExpr->getType(); - // See also HandleIntToIntCast in ExprConstant.cpp - if (FromType->isIntegerType()) - if (llvm::Constant *C = Visit(subExpr, FromType)) - if (auto *CI = dyn_cast<llvm::ConstantInt>(C)) { - unsigned SrcWidth = CGM.getContext().getIntWidth(FromType); - unsigned DstWidth = CGM.getContext().getIntWidth(destType); - if (DstWidth == SrcWidth) - return CI; - llvm::APInt A = FromType->isSignedIntegerType() - ? CI->getValue().sextOrTrunc(DstWidth) - : CI->getValue().zextOrTrunc(DstWidth); - return llvm::ConstantInt::get(CGM.getLLVMContext(), A); - } - return nullptr; - } + case CK_IntegralCast: + return ProduceIntToIntCast(subExpr, destType); case CK_Dependent: llvm_unreachable("saw dependent cast!"); @@ -1249,15 +1252,42 @@ public: return llvm::ConstantInt::get(CGM.getLLVMContext(), I->getValue()); } + static APValue withDestType(ASTContext &Ctx, const Expr *E, QualType SrcType, + QualType DestType, const llvm::APSInt &Value) { + if (!Ctx.hasSameType(SrcType, DestType)) { + if (DestType->isFloatingType()) { + llvm::APFloat Result = + llvm::APFloat(Ctx.getFloatTypeSemantics(DestType), 1); + llvm::RoundingMode RM = + E->getFPFeaturesInEffect(Ctx.getLangOpts()).getRoundingMode(); + if (RM == llvm::RoundingMode::Dynamic) + RM = llvm::RoundingMode::NearestTiesToEven; + Result.convertFromAPInt(Value, Value.isSigned(), RM); + return APValue(Result); + } + } + return APValue(Value); + } + llvm::Constant *EmitArrayInitialization(const InitListExpr *ILE, QualType T) { auto *CAT = CGM.getContext().getAsConstantArrayType(ILE->getType()); assert(CAT && "can't emit array init for non-constant-bound array"); + uint64_t NumInitElements = ILE->getNumInits(); const uint64_t NumElements = CAT->getZExtSize(); + for (const auto *Init : ILE->inits()) { + if (const auto *Embed = + dyn_cast<EmbedExpr>(Init->IgnoreParenImpCasts())) { + NumInitElements += Embed->getDataElementCount() - 1; + if (NumInitElements > NumElements) { + NumInitElements = NumElements; + break; + } + } + } // Initialising an array requires us to automatically // initialise any elements that have not been initialised explicitly - uint64_t NumInitableElts = - std::min<uint64_t>(ILE->getNumInits(), NumElements); + uint64_t NumInitableElts = std::min<uint64_t>(NumInitElements, NumElements); QualType EltType = CAT->getElementType(); @@ -1270,23 +1300,61 @@ public: } // Copy initializer elements. - SmallVector<llvm::Constant*, 16> Elts; + SmallVector<llvm::Constant *, 16> Elts; if (fillC && fillC->isNullValue()) Elts.reserve(NumInitableElts + 1); else Elts.reserve(NumElements); llvm::Type *CommonElementType = nullptr; - for (unsigned i = 0; i < NumInitableElts; ++i) { - const Expr *Init = ILE->getInit(i); - llvm::Constant *C = Emitter.tryEmitPrivateForMemory(Init, EltType); + auto Emit = [&](const Expr *Init, unsigned ArrayIndex) { + llvm::Constant *C = nullptr; + C = Emitter.tryEmitPrivateForMemory(Init, EltType); if (!C) - return nullptr; - if (i == 0) + return false; + if (ArrayIndex == 0) CommonElementType = C->getType(); else if (C->getType() != CommonElementType) CommonElementType = nullptr; Elts.push_back(C); + return true; + }; + + unsigned ArrayIndex = 0; + QualType DestTy = CAT->getElementType(); + for (unsigned i = 0; i < ILE->getNumInits(); ++i) { + const Expr *Init = ILE->getInit(i); + if (auto *EmbedS = dyn_cast<EmbedExpr>(Init->IgnoreParenImpCasts())) { + StringLiteral *SL = EmbedS->getDataStringLiteral(); + llvm::APSInt Value(CGM.getContext().getTypeSize(DestTy), + DestTy->isUnsignedIntegerType()); + llvm::Constant *C; + for (unsigned I = EmbedS->getStartingElementPos(), + N = EmbedS->getDataElementCount(); + I != EmbedS->getStartingElementPos() + N; ++I) { + Value = SL->getCodeUnit(I); + if (DestTy->isIntegerType()) { + C = llvm::ConstantInt::get(CGM.getLLVMContext(), Value); + } else { + C = Emitter.tryEmitPrivateForMemory( + withDestType(CGM.getContext(), Init, EmbedS->getType(), DestTy, + Value), + EltType); + } + if (!C) + return nullptr; + Elts.push_back(C); + ArrayIndex++; + } + if ((ArrayIndex - EmbedS->getDataElementCount()) == 0) + CommonElementType = C->getType(); + else if (C->getType() != CommonElementType) + CommonElementType = nullptr; + } else { + if (!Emit(Init, ArrayIndex)) + return nullptr; + ArrayIndex++; + } } llvm::ArrayType *Desired = diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 1b144c1..cbbe9fa 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -506,6 +506,7 @@ public: } Value *VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E); + Value *VisitEmbedExpr(EmbedExpr *E); Value *VisitOpaqueValueExpr(OpaqueValueExpr *E) { if (E->isGLValue()) @@ -1796,6 +1797,12 @@ ScalarExprEmitter::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) { "usn_addr_cast"); } +Value *ScalarExprEmitter::VisitEmbedExpr(EmbedExpr *E) { + assert(E->getDataElementCount() == 1); + auto It = E->begin(); + return Builder.getInt((*It)->getValue()); +} + Value *ScalarExprEmitter::VisitShuffleVectorExpr(ShuffleVectorExpr *E) { // Vector Mask Case if (E->getNumSubExprs() == 2) { diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index b8d8ff3..1f85915 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1220,7 +1220,8 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, Args.addAllArgs(CmdArgs, {options::OPT_D, options::OPT_U, options::OPT_I_Group, - options::OPT_F, options::OPT_index_header_map}); + options::OPT_F, options::OPT_index_header_map, + options::OPT_embed_dir_EQ}); // Add -Wp, and -Xpreprocessor if using the preprocessor. @@ -8505,6 +8506,9 @@ void ClangAs::ConstructJob(Compilation &C, const JobAction &JA, // Pass along any -I options so we get proper .include search paths. Args.AddAllArgs(CmdArgs, options::OPT_I_Group); + // Pass along any --embed-dir or similar options so we get proper embed paths. + Args.AddAllArgs(CmdArgs, options::OPT_embed_dir_EQ); + // Determine the original source input. auto FindSource = [](const Action *S) -> const Action * { while (S->getKind() != Action::InputClass) { diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 58694e5..cde4a84 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -4492,6 +4492,9 @@ static void GeneratePreprocessorArgs(const PreprocessorOptions &Opts, if (Opts.DefineTargetOSMacros) GenerateArg(Consumer, OPT_fdefine_target_os_macros); + for (const auto &EmbedEntry : Opts.EmbedEntries) + GenerateArg(Consumer, OPT_embed_dir_EQ, EmbedEntry); + // Don't handle LexEditorPlaceholders. It is implied by the action that is // generated elsewhere. } @@ -4584,6 +4587,11 @@ static bool ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args, } } + for (const auto *A : Args.filtered(OPT_embed_dir_EQ)) { + StringRef Val = A->getValue(); + Opts.EmbedEntries.push_back(std::string(Val)); + } + // Always avoid lexing editor placeholders when we're just running the // preprocessor as we never want to emit the // "editor placeholder in source file" error in PP only mode. diff --git a/clang/lib/Frontend/DependencyFile.cpp b/clang/lib/Frontend/DependencyFile.cpp index 369816e..528eae2 100644 --- a/clang/lib/Frontend/DependencyFile.cpp +++ b/clang/lib/Frontend/DependencyFile.cpp @@ -62,6 +62,19 @@ struct DepCollectorPPCallbacks : public PPCallbacks { /*IsMissing=*/false); } + void EmbedDirective(SourceLocation, StringRef, bool, + OptionalFileEntryRef File, + const LexEmbedParametersResult &) override { + assert(File && "expected to only be called when the file is found"); + StringRef FileName = + llvm::sys::path::remove_leading_dotslash(File->getName()); + DepCollector.maybeAddDependency(FileName, + /*FromModule*/ false, + /*IsSystem*/ false, + /*IsModuleFile*/ false, + /*IsMissing*/ false); + } + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, @@ -77,6 +90,18 @@ struct DepCollectorPPCallbacks : public PPCallbacks { // Files that actually exist are handled by FileChanged. } + void HasEmbed(SourceLocation, StringRef, bool, + OptionalFileEntryRef File) override { + if (!File) + return; + StringRef Filename = + llvm::sys::path::remove_leading_dotslash(File->getName()); + DepCollector.maybeAddDependency(Filename, + /*FromModule=*/false, false, + /*IsModuleFile=*/false, + /*IsMissing=*/false); + } + void HasInclude(SourceLocation Loc, StringRef SpelledFilename, bool IsAngled, OptionalFileEntryRef File, SrcMgr::CharacteristicKind FileType) override { diff --git a/clang/lib/Frontend/DependencyGraph.cpp b/clang/lib/Frontend/DependencyGraph.cpp index 20e5f23..c23ce66 100644 --- a/clang/lib/Frontend/DependencyGraph.cpp +++ b/clang/lib/Frontend/DependencyGraph.cpp @@ -43,7 +43,7 @@ private: public: DependencyGraphCallback(const Preprocessor *_PP, StringRef OutputFile, StringRef SysRoot) - : PP(_PP), OutputFile(OutputFile.str()), SysRoot(SysRoot.str()) { } + : PP(_PP), OutputFile(OutputFile.str()), SysRoot(SysRoot.str()) {} void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, bool IsAngled, @@ -53,6 +53,10 @@ public: bool ModuleImported, SrcMgr::CharacteristicKind FileType) override; + void EmbedDirective(SourceLocation HashLoc, StringRef FileName, bool IsAngled, + OptionalFileEntryRef File, + const LexEmbedParametersResult &Params) override; + void EndOfMainFile() override { OutputGraphFile(); } @@ -86,6 +90,24 @@ void DependencyGraphCallback::InclusionDirective( AllFiles.insert(*FromFile); } +void DependencyGraphCallback::EmbedDirective(SourceLocation HashLoc, StringRef, + bool, OptionalFileEntryRef File, + const LexEmbedParametersResult &) { + if (!File) + return; + + SourceManager &SM = PP->getSourceManager(); + OptionalFileEntryRef FromFile = + SM.getFileEntryRefForID(SM.getFileID(SM.getExpansionLoc(HashLoc))); + if (!FromFile) + return; + + Dependencies[*FromFile].push_back(*File); + + AllFiles.insert(*File); + AllFiles.insert(*FromFile); +} + raw_ostream & DependencyGraphCallback::writeNodeReference(raw_ostream &OS, const FileEntry *Node) { diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index e8c8a51..2d5c94c 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -508,6 +508,14 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI, Builder.defineMacro("__STDC_UTF_16__", "1"); Builder.defineMacro("__STDC_UTF_32__", "1"); + // __has_embed definitions + Builder.defineMacro("__STDC_EMBED_NOT_FOUND__", + llvm::itostr(static_cast<int>(EmbedResult::NotFound))); + Builder.defineMacro("__STDC_EMBED_FOUND__", + llvm::itostr(static_cast<int>(EmbedResult::Found))); + Builder.defineMacro("__STDC_EMBED_EMPTY__", + llvm::itostr(static_cast<int>(EmbedResult::Empty))); + if (LangOpts.ObjC) Builder.defineMacro("__OBJC__"); diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp index a26d2c3..0592423 100644 --- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// -#include "clang/Frontend/Utils.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/Frontend/PreprocessorOutputOptions.h" +#include "clang/Frontend/Utils.h" #include "clang/Lex/MacroInfo.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Pragma.h" @@ -93,6 +93,7 @@ private: bool DisableLineMarkers; bool DumpDefines; bool DumpIncludeDirectives; + bool DumpEmbedDirectives; bool UseLineDirectives; bool IsFirstFileEntered; bool MinimizeWhitespace; @@ -100,6 +101,7 @@ private: bool KeepSystemIncludes; raw_ostream *OrigOS; std::unique_ptr<llvm::raw_null_ostream> NullOS; + unsigned NumToksToSkip; Token PrevTok; Token PrevPrevTok; @@ -107,14 +109,16 @@ private: public: PrintPPOutputPPCallbacks(Preprocessor &pp, raw_ostream *os, bool lineMarkers, bool defines, bool DumpIncludeDirectives, - bool UseLineDirectives, bool MinimizeWhitespace, - bool DirectivesOnly, bool KeepSystemIncludes) + bool DumpEmbedDirectives, bool UseLineDirectives, + bool MinimizeWhitespace, bool DirectivesOnly, + bool KeepSystemIncludes) : PP(pp), SM(PP.getSourceManager()), ConcatInfo(PP), OS(os), DisableLineMarkers(lineMarkers), DumpDefines(defines), DumpIncludeDirectives(DumpIncludeDirectives), + DumpEmbedDirectives(DumpEmbedDirectives), UseLineDirectives(UseLineDirectives), MinimizeWhitespace(MinimizeWhitespace), DirectivesOnly(DirectivesOnly), - KeepSystemIncludes(KeepSystemIncludes), OrigOS(os) { + KeepSystemIncludes(KeepSystemIncludes), OrigOS(os), NumToksToSkip(0) { CurLine = 0; CurFilename += "<uninit>"; EmittedTokensOnThisLine = false; @@ -129,6 +133,10 @@ public: PrevPrevTok.startToken(); } + /// Returns true if #embed directives should be expanded into a comma- + /// delimited list of integer constants or not. + bool expandEmbedContents() const { return !DumpEmbedDirectives; } + bool isMinimizeWhitespace() const { return MinimizeWhitespace; } void setEmittedTokensOnThisLine() { EmittedTokensOnThisLine = true; } @@ -149,6 +157,9 @@ public: void FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID) override; + void EmbedDirective(SourceLocation HashLoc, StringRef FileName, bool IsAngled, + OptionalFileEntryRef File, + const LexEmbedParametersResult &Params) override; void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, @@ -232,6 +243,9 @@ public: void BeginModule(const Module *M); void EndModule(const Module *M); + + unsigned GetNumToksToSkip() const { return NumToksToSkip; } + void ResetSkipToks() { NumToksToSkip = 0; } }; } // end anonymous namespace @@ -399,6 +413,74 @@ void PrintPPOutputPPCallbacks::FileChanged(SourceLocation Loc, } } +void PrintPPOutputPPCallbacks::EmbedDirective( + SourceLocation HashLoc, StringRef FileName, bool IsAngled, + OptionalFileEntryRef File, const LexEmbedParametersResult &Params) { + if (!DumpEmbedDirectives) + return; + + // The EmbedDirective() callback is called before we produce the annotation + // token stream for the directive. We skip printing the annotation tokens + // within PrintPreprocessedTokens(), but we also need to skip the prefix, + // suffix, and if_empty tokens as those are inserted directly into the token + // stream and would otherwise be printed immediately after printing the + // #embed directive. + // + // FIXME: counting tokens to skip is a kludge but we have no way to know + // which tokens were inserted as part of the embed and which ones were + // explicitly written by the user. + MoveToLine(HashLoc, /*RequireStartOfLine=*/true); + *OS << "#embed " << (IsAngled ? '<' : '"') << FileName + << (IsAngled ? '>' : '"'); + + auto PrintToks = [&](llvm::ArrayRef<Token> Toks) { + SmallString<128> SpellingBuffer; + for (const Token &T : Toks) { + if (T.hasLeadingSpace()) + *OS << " "; + *OS << PP.getSpelling(T, SpellingBuffer); + } + }; + bool SkipAnnotToks = true; + if (Params.MaybeIfEmptyParam) { + *OS << " if_empty("; + PrintToks(Params.MaybeIfEmptyParam->Tokens); + *OS << ")"; + // If the file is empty, we can skip those tokens. If the file is not + // empty, we skip the annotation tokens. + if (File && !File->getSize()) { + NumToksToSkip += Params.MaybeIfEmptyParam->Tokens.size(); + SkipAnnotToks = false; + } + } + + if (Params.MaybeLimitParam) { + *OS << " limit(" << Params.MaybeLimitParam->Limit << ")"; + } + if (Params.MaybeOffsetParam) { + *OS << " clang::offset(" << Params.MaybeOffsetParam->Offset << ")"; + } + if (Params.MaybePrefixParam) { + *OS << " prefix("; + PrintToks(Params.MaybePrefixParam->Tokens); + *OS << ")"; + NumToksToSkip += Params.MaybePrefixParam->Tokens.size(); + } + if (Params.MaybeSuffixParam) { + *OS << " suffix("; + PrintToks(Params.MaybeSuffixParam->Tokens); + *OS << ")"; + NumToksToSkip += Params.MaybeSuffixParam->Tokens.size(); + } + + // We may need to skip the annotation token. + if (SkipAnnotToks) + NumToksToSkip++; + + *OS << " /* clang -E -dE */"; + setEmittedDirectiveOnThisLine(); +} + void PrintPPOutputPPCallbacks::InclusionDirective( SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File, @@ -678,7 +760,7 @@ void PrintPPOutputPPCallbacks::HandleWhitespaceBeforeTok(const Token &Tok, if (Tok.is(tok::eof) || (Tok.isAnnotation() && !Tok.is(tok::annot_header_unit) && !Tok.is(tok::annot_module_begin) && !Tok.is(tok::annot_module_end) && - !Tok.is(tok::annot_repl_input_end))) + !Tok.is(tok::annot_repl_input_end) && !Tok.is(tok::annot_embed))) return; // EmittedDirectiveOnThisLine takes priority over RequireSameLine. @@ -878,6 +960,27 @@ 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::annot_embed)) { + // Manually explode the binary data out to a stream of comma-delimited + // integer values. If the user passed -dE, that is handled by the + // EmbedDirective() callback. We should only get here if the user did not + // pass -dE. + assert(Callbacks->expandEmbedContents() && + "did not expect an embed annotation"); + auto *Data = + reinterpret_cast<EmbedAnnotationData *>(Tok.getAnnotationValue()); + + // Loop over the contents and print them as a comma-delimited list of + // values. + bool PrintComma = false; + for (auto Iter = Data->BinaryData.begin(), End = Data->BinaryData.end(); + Iter != End; ++Iter) { + if (PrintComma) + *Callbacks->OS << ", "; + *Callbacks->OS << static_cast<unsigned>(*Iter); + PrintComma = true; + } + IsStartOfLine = true; } else if (Tok.isAnnotation()) { // Ignore annotation tokens created by pragmas - the pragmas themselves // will be reproduced in the preprocessed output. @@ -926,6 +1029,10 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok, if (Tok.is(tok::eof)) break; PP.Lex(Tok); + // If lexing that token causes us to need to skip future tokens, do so now. + for (unsigned I = 0, Skip = Callbacks->GetNumToksToSkip(); I < Skip; ++I) + PP.Lex(Tok); + Callbacks->ResetSkipToks(); } } @@ -982,8 +1089,9 @@ void clang::DoPrintPreprocessedInput(Preprocessor &PP, raw_ostream *OS, PrintPPOutputPPCallbacks *Callbacks = new PrintPPOutputPPCallbacks( PP, OS, !Opts.ShowLineMarkers, Opts.ShowMacros, - Opts.ShowIncludeDirectives, Opts.UseLineDirectives, - Opts.MinimizeWhitespace, Opts.DirectivesOnly, Opts.KeepSystemIncludes); + Opts.ShowIncludeDirectives, Opts.ShowEmbedDirectives, + Opts.UseLineDirectives, Opts.MinimizeWhitespace, Opts.DirectivesOnly, + Opts.KeepSystemIncludes); // Expand macros in pragmas with -fms-extensions. The assumption is that // the majority of pragmas in such a file will be Microsoft pragmas. diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 8e73864..b7ee0c0 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -19,6 +19,7 @@ #include "clang/Basic/Module.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" #include "clang/Basic/TokenKinds.h" #include "clang/Lex/CodeCompletionHandler.h" #include "clang/Lex/HeaderSearch.h" @@ -39,6 +40,7 @@ #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/AlignOf.h" @@ -82,8 +84,7 @@ Preprocessor::AllocateVisibilityMacroDirective(SourceLocation Loc, /// Read and discard all tokens remaining on the current line until /// the tok::eod token is found. -SourceRange Preprocessor::DiscardUntilEndOfDirective() { - Token Tmp; +SourceRange Preprocessor::DiscardUntilEndOfDirective(Token &Tmp) { SourceRange Res; LexUnexpandedToken(Tmp); @@ -1073,6 +1074,74 @@ OptionalFileEntryRef Preprocessor::LookupFile( return std::nullopt; } +OptionalFileEntryRef +Preprocessor::LookupEmbedFile(StringRef Filename, bool isAngled, bool OpenFile, + const FileEntry *LookupFromFile) { + FileManager &FM = this->getFileManager(); + if (llvm::sys::path::is_absolute(Filename)) { + // lookup path or immediately fail + llvm::Expected<FileEntryRef> ShouldBeEntry = + FM.getFileRef(Filename, OpenFile); + 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) { + if (LookupFromFile) { + // Use file-based lookup. + StringRef FullFileDir = LookupFromFile->tryGetRealPathName(); + if (!FullFileDir.empty()) { + SeparateComponents(LookupPath, FullFileDir, Filename, true); + llvm::Expected<FileEntryRef> ShouldBeEntry = + FM.getFileRef(LookupPath, OpenFile); + 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); + 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); + return llvm::expectedToOptional(std::move(ShouldBeEntry)); + } + return std::nullopt; +} + //===----------------------------------------------------------------------===// // Preprocessor Directive Handling. //===----------------------------------------------------------------------===// @@ -1168,6 +1237,7 @@ void Preprocessor::HandleDirective(Token &Result) { case tok::pp_include_next: case tok::pp___include_macros: case tok::pp_pragma: + case tok::pp_embed: Diag(Result, diag::err_embedded_directive) << II->getName(); Diag(*ArgMacro, diag::note_macro_expansion_here) << ArgMacro->getIdentifierInfo(); @@ -1282,6 +1352,11 @@ void Preprocessor::HandleDirective(Token &Result) { return HandleIdentSCCSDirective(Result); case tok::pp_sccs: return HandleIdentSCCSDirective(Result); + case tok::pp_embed: + return HandleEmbedDirective(SavedHash.getLocation(), Result, + getCurrentFileLexer() + ? *getCurrentFileLexer()->getFileEntry() + : static_cast<FileEntry *>(nullptr)); case tok::pp_assert: //isExtension = true; // FIXME: implement #assert break; @@ -3543,3 +3618,401 @@ void Preprocessor::HandleElifFamilyDirective(Token &ElifToken, HashToken.getLocation(), CI.IfLoc, /*Foundnonskip*/ true, /*FoundElse*/ CI.FoundElse, ElifToken.getLocation()); } + +std::optional<LexEmbedParametersResult> +Preprocessor::LexEmbedParameters(Token &CurTok, bool ForHasEmbed) { + LexEmbedParametersResult Result{}; + SmallVector<Token, 2> ParameterTokens; + tok::TokenKind EndTokenKind = ForHasEmbed ? tok::r_paren : tok::eod; + Result.ParamRange = {CurTok.getLocation(), CurTok.getLocation()}; + + auto DiagMismatchedBracesAndSkipToEOD = + [&](tok::TokenKind Expected, + std::pair<tok::TokenKind, SourceLocation> Matches) { + Result.ParamRange.setEnd(CurTok.getEndLoc()); + Diag(CurTok, diag::err_expected) << Expected; + Diag(Matches.second, diag::note_matching) << Matches.first; + if (CurTok.isNot(tok::eod)) + DiscardUntilEndOfDirective(CurTok); + }; + + auto ExpectOrDiagAndSkipToEOD = [&](tok::TokenKind Kind) { + if (CurTok.isNot(Kind)) { + Result.ParamRange.setEnd(CurTok.getEndLoc()); + Diag(CurTok, diag::err_expected) << Kind; + if (CurTok.isNot(tok::eod)) + DiscardUntilEndOfDirective(CurTok); + return false; + } + return true; + }; + + // C23 6.10: + // pp-parameter-name: + // pp-standard-parameter + // pp-prefixed-parameter + // + // pp-standard-parameter: + // identifier + // + // pp-prefixed-parameter: + // identifier :: identifier + auto LexPPParameterName = [&]() -> std::optional<std::string> { + // We expect the current token to be an identifier; if it's not, things + // have gone wrong. + if (!ExpectOrDiagAndSkipToEOD(tok::identifier)) + return std::nullopt; + + const IdentifierInfo *Prefix = CurTok.getIdentifierInfo(); + + // Lex another token; it is either a :: or we're done with the parameter + // name. + LexNonComment(CurTok); + if (CurTok.is(tok::coloncolon)) { + // We found a ::, so lex another identifier token. + LexNonComment(CurTok); + if (!ExpectOrDiagAndSkipToEOD(tok::identifier)) + return std::nullopt; + + const IdentifierInfo *Suffix = CurTok.getIdentifierInfo(); + + // Lex another token so we're past the name. + LexNonComment(CurTok); + return (llvm::Twine(Prefix->getName()) + "::" + Suffix->getName()).str(); + } + return Prefix->getName().str(); + }; + + // C23 6.10p5: In all aspects, a preprocessor standard parameter specified by + // this document as an identifier pp_param and an identifier of the form + // __pp_param__ shall behave the same when used as a preprocessor parameter, + // except for the spelling. + auto NormalizeParameterName = [](StringRef Name) { + if (Name.size() > 4 && Name.starts_with("__") && Name.ends_with("__")) + return Name.substr(2, Name.size() - 4); + return Name; + }; + + auto LexParenthesizedIntegerExpr = [&]() -> std::optional<size_t> { + // we have a limit parameter and its internals are processed using + // evaluation rules from #if. + if (!ExpectOrDiagAndSkipToEOD(tok::l_paren)) + return std::nullopt; + + // We do not consume the ( because EvaluateDirectiveExpression will lex + // the next token for us. + IdentifierInfo *ParameterIfNDef = nullptr; + bool EvaluatedDefined; + DirectiveEvalResult LimitEvalResult = EvaluateDirectiveExpression( + ParameterIfNDef, CurTok, EvaluatedDefined, /*CheckForEOD=*/false); + + if (!LimitEvalResult.Value) { + // If there was an error evaluating the directive expression, we expect + // to be at the end of directive token. + assert(CurTok.is(tok::eod) && "expect to be at the end of directive"); + return std::nullopt; + } + + if (!ExpectOrDiagAndSkipToEOD(tok::r_paren)) + return std::nullopt; + + // Eat the ). + LexNonComment(CurTok); + + // C23 6.10.3.2p2: The token defined shall not appear within the constant + // expression. + if (EvaluatedDefined) { + Diag(CurTok, diag::err_defined_in_pp_embed); + return std::nullopt; + } + + if (LimitEvalResult.Value) { + const llvm::APSInt &Result = *LimitEvalResult.Value; + if (Result.isNegative()) { + Diag(CurTok, diag::err_requires_positive_value) + << toString(Result, 10) << /*positive*/ 0; + return std::nullopt; + } + return Result.getLimitedValue(); + } + return std::nullopt; + }; + + auto GetMatchingCloseBracket = [](tok::TokenKind Kind) { + switch (Kind) { + case tok::l_paren: + return tok::r_paren; + case tok::l_brace: + return tok::r_brace; + case tok::l_square: + return tok::r_square; + default: + llvm_unreachable("should not get here"); + } + }; + + auto LexParenthesizedBalancedTokenSoup = + [&](llvm::SmallVectorImpl<Token> &Tokens) { + std::vector<std::pair<tok::TokenKind, SourceLocation>> BracketStack; + + // We expect the current token to be a left paren. + if (!ExpectOrDiagAndSkipToEOD(tok::l_paren)) + return false; + LexNonComment(CurTok); // Eat the ( + + bool WaitingForInnerCloseParen = false; + while (CurTok.isNot(tok::eod) && + (WaitingForInnerCloseParen || + (!WaitingForInnerCloseParen && CurTok.isNot(tok::r_paren)))) { + switch (CurTok.getKind()) { + default: // Shutting up diagnostics about not fully-covered switch. + break; + case tok::l_paren: + WaitingForInnerCloseParen = true; + [[fallthrough]]; + case tok::l_brace: + case tok::l_square: + BracketStack.push_back({CurTok.getKind(), CurTok.getLocation()}); + break; + case tok::r_paren: + WaitingForInnerCloseParen = false; + [[fallthrough]]; + case tok::r_brace: + case tok::r_square: { + tok::TokenKind Matching = + GetMatchingCloseBracket(BracketStack.back().first); + if (BracketStack.empty() || CurTok.getKind() != Matching) { + DiagMismatchedBracesAndSkipToEOD(Matching, BracketStack.back()); + return false; + } + BracketStack.pop_back(); + } break; + } + Tokens.push_back(CurTok); + LexNonComment(CurTok); + } + + // When we're done, we want to eat the closing paren. + if (!ExpectOrDiagAndSkipToEOD(tok::r_paren)) + return false; + + LexNonComment(CurTok); // Eat the ) + return true; + }; + + LexNonComment(CurTok); // Prime the pump. + while (!CurTok.isOneOf(EndTokenKind, tok::eod)) { + SourceLocation ParamStartLoc = CurTok.getLocation(); + std::optional<std::string> ParamName = LexPPParameterName(); + if (!ParamName) + return std::nullopt; + StringRef Parameter = NormalizeParameterName(*ParamName); + + // Lex the parameters (dependent on the parameter type we want!). + // + // C23 6.10.3.Xp1: The X standard embed parameter may appear zero times or + // one time in the embed parameter sequence. + if (Parameter == "limit") { + if (Result.MaybeLimitParam) + Diag(CurTok, diag::err_pp_embed_dup_params) << Parameter; + + std::optional<size_t> Limit = LexParenthesizedIntegerExpr(); + if (!Limit) + return std::nullopt; + Result.MaybeLimitParam = + PPEmbedParameterLimit{*Limit, {ParamStartLoc, CurTok.getLocation()}}; + } else if (Parameter == "clang::offset") { + if (Result.MaybeOffsetParam) + Diag(CurTok, diag::err_pp_embed_dup_params) << Parameter; + + std::optional<size_t> Offset = LexParenthesizedIntegerExpr(); + if (!Offset) + return std::nullopt; + Result.MaybeOffsetParam = PPEmbedParameterOffset{ + *Offset, {ParamStartLoc, CurTok.getLocation()}}; + } else if (Parameter == "prefix") { + if (Result.MaybePrefixParam) + Diag(CurTok, diag::err_pp_embed_dup_params) << Parameter; + + SmallVector<Token, 4> Soup; + if (!LexParenthesizedBalancedTokenSoup(Soup)) + return std::nullopt; + Result.MaybePrefixParam = PPEmbedParameterPrefix{ + std::move(Soup), {ParamStartLoc, CurTok.getLocation()}}; + } else if (Parameter == "suffix") { + if (Result.MaybeSuffixParam) + Diag(CurTok, diag::err_pp_embed_dup_params) << Parameter; + + SmallVector<Token, 4> Soup; + if (!LexParenthesizedBalancedTokenSoup(Soup)) + return std::nullopt; + Result.MaybeSuffixParam = PPEmbedParameterSuffix{ + std::move(Soup), {ParamStartLoc, CurTok.getLocation()}}; + } else if (Parameter == "if_empty") { + if (Result.MaybeIfEmptyParam) + Diag(CurTok, diag::err_pp_embed_dup_params) << Parameter; + + SmallVector<Token, 4> Soup; + if (!LexParenthesizedBalancedTokenSoup(Soup)) + return std::nullopt; + Result.MaybeIfEmptyParam = PPEmbedParameterIfEmpty{ + std::move(Soup), {ParamStartLoc, CurTok.getLocation()}}; + } else { + ++Result.UnrecognizedParams; + + // If there's a left paren, we need to parse a balanced token sequence + // and just eat those tokens. + if (CurTok.is(tok::l_paren)) { + SmallVector<Token, 4> Soup; + if (!LexParenthesizedBalancedTokenSoup(Soup)) + return std::nullopt; + } + if (!ForHasEmbed) { + Diag(CurTok, diag::err_pp_unknown_parameter) << 1 << Parameter; + return std::nullopt; + } + } + } + Result.ParamRange.setEnd(CurTok.getLocation()); + return Result; +} + +void Preprocessor::HandleEmbedDirectiveImpl( + SourceLocation HashLoc, StringRef ResolvedFilename, + const LexEmbedParametersResult &Params, StringRef BinaryContents) { + if (BinaryContents.empty()) { + // If we have no binary contents, the only thing we need to emit are the + // if_empty tokens, if any. + // FIXME: this loses AST fidelity; nothing in the compiler will see that + // these tokens came from #embed. We have to hack around this when printing + // preprocessed output. The same is true for prefix and suffix tokens. + if (Params.MaybeIfEmptyParam) { + ArrayRef<Token> Toks = Params.MaybeIfEmptyParam->Tokens; + size_t TokCount = Toks.size(); + auto NewToks = std::make_unique<Token[]>(TokCount); + llvm::copy(Toks, NewToks.get()); + EnterTokenStream(std::move(NewToks), TokCount, true, true); + } + return; + } + + size_t NumPrefixToks = Params.PrefixTokenCount(), + NumSuffixToks = Params.SuffixTokenCount(); + size_t TotalNumToks = 1 + NumPrefixToks + NumSuffixToks; + size_t CurIdx = 0; + auto Toks = std::make_unique<Token[]>(TotalNumToks); + + // Add the prefix tokens, if any. + if (Params.MaybePrefixParam) { + llvm::copy(Params.MaybePrefixParam->Tokens, &Toks[CurIdx]); + CurIdx += NumPrefixToks; + } + + EmbedAnnotationData *Data = new (BP) EmbedAnnotationData; + Data->FileName = ResolvedFilename; + Data->BinaryData = BinaryContents; + + Toks[CurIdx].startToken(); + Toks[CurIdx].setKind(tok::annot_embed); + Toks[CurIdx].setAnnotationRange(HashLoc); + Toks[CurIdx++].setAnnotationValue(Data); + + // Now add the suffix tokens, if any. + if (Params.MaybeSuffixParam) { + llvm::copy(Params.MaybeSuffixParam->Tokens, &Toks[CurIdx]); + CurIdx += NumSuffixToks; + } + + assert(CurIdx == TotalNumToks && "Calculated the incorrect number of tokens"); + EnterTokenStream(std::move(Toks), TotalNumToks, true, true); +} + +void Preprocessor::HandleEmbedDirective(SourceLocation HashLoc, Token &EmbedTok, + const FileEntry *LookupFromFile) { + // Give the usual extension/compatibility warnings. + if (LangOpts.C23) + Diag(EmbedTok, diag::warn_compat_pp_embed_directive); + else + Diag(EmbedTok, diag::ext_pp_embed_directive) + << (LangOpts.CPlusPlus ? /*Clang*/ 1 : /*C23*/ 0); + + // Parse the filename header + Token FilenameTok; + if (LexHeaderName(FilenameTok)) + return; + + if (FilenameTok.isNot(tok::header_name)) { + Diag(FilenameTok.getLocation(), diag::err_pp_expects_filename); + if (FilenameTok.isNot(tok::eod)) + DiscardUntilEndOfDirective(); + return; + } + + // Parse the optional sequence of + // directive-parameters: + // identifier parameter-name-list[opt] directive-argument-list[opt] + // directive-argument-list: + // '(' balanced-token-sequence ')' + // parameter-name-list: + // '::' identifier parameter-name-list[opt] + Token CurTok; + std::optional<LexEmbedParametersResult> Params = + LexEmbedParameters(CurTok, /*ForHasEmbed=*/false); + + assert((Params || CurTok.is(tok::eod)) && + "expected success or to be at the end of the directive"); + if (!Params) + return; + + // Now, splat the data out! + SmallString<128> FilenameBuffer; + StringRef Filename = getSpelling(FilenameTok, FilenameBuffer); + StringRef OriginalFilename = Filename; + bool isAngled = + GetIncludeFilenameSpelling(FilenameTok.getLocation(), Filename); + // If GetIncludeFilenameSpelling set the start ptr to null, there was an + // error. + assert(!Filename.empty()); + OptionalFileEntryRef MaybeFileRef = + this->LookupEmbedFile(Filename, isAngled, true, LookupFromFile); + if (!MaybeFileRef) { + // could not find file + if (Callbacks && Callbacks->EmbedFileNotFound(OriginalFilename)) { + return; + } + Diag(FilenameTok, diag::err_pp_file_not_found) << Filename; + return; + } + std::optional<llvm::MemoryBufferRef> MaybeFile = + getSourceManager().getMemoryBufferForFileOrNone(*MaybeFileRef); + if (!MaybeFile) { + // could not find file + Diag(FilenameTok, diag::err_cannot_open_file) + << Filename << "a buffer to the contents could not be created"; + return; + } + StringRef BinaryContents = MaybeFile->getBuffer(); + + // The order is important between 'offset' and 'limit'; we want to offset + // first and then limit second; otherwise we may reduce the notional resource + // size to something too small to offset into. + if (Params->MaybeOffsetParam) { + // FIXME: just like with the limit() and if_empty() parameters, this loses + // source fidelity in the AST; it has no idea that there was an offset + // involved. + // offsets all the way to the end of the file make for an empty file. + BinaryContents = BinaryContents.substr(Params->MaybeOffsetParam->Offset); + } + + if (Params->MaybeLimitParam) { + // FIXME: just like with the clang::offset() and if_empty() parameters, + // this loses source fidelity in the AST; it has no idea there was a limit + // involved. + BinaryContents = BinaryContents.substr(0, Params->MaybeLimitParam->Limit); + } + + if (Callbacks) + Callbacks->EmbedDirective(HashLoc, Filename, isAngled, MaybeFileRef, + *Params); + HandleEmbedDirectiveImpl(HashLoc, Filename, *Params, BinaryContents); +} diff --git a/clang/lib/Lex/PPExpressions.cpp b/clang/lib/Lex/PPExpressions.cpp index f267efabd..8bb82bd 100644 --- a/clang/lib/Lex/PPExpressions.cpp +++ b/clang/lib/Lex/PPExpressions.cpp @@ -870,7 +870,9 @@ static bool EvaluateDirectiveSubExpr(PPValue &LHS, unsigned MinPrec, /// may occur after a #if or #elif directive. If the expression is equivalent /// to "!defined(X)" return X in IfNDefMacro. Preprocessor::DirectiveEvalResult -Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) { +Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro, + Token &Tok, bool &EvaluatedDefined, + bool CheckForEoD) { SaveAndRestore PPDir(ParsingIfOrElifDirective, true); // Save the current state of 'DisableMacroExpansion' and reset it to false. If // 'DisableMacroExpansion' is true, then we must be in a macro argument list @@ -882,7 +884,6 @@ Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) { DisableMacroExpansion = false; // Peek ahead one token. - Token Tok; LexNonComment(Tok); // C99 6.10.1p3 - All expressions are evaluated as intmax_t or uintmax_t. @@ -895,7 +896,7 @@ Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) { // Parse error, skip the rest of the macro line. SourceRange ConditionRange = ExprStartLoc; if (Tok.isNot(tok::eod)) - ConditionRange = DiscardUntilEndOfDirective(); + ConditionRange = DiscardUntilEndOfDirective(Tok); // Restore 'DisableMacroExpansion'. DisableMacroExpansion = DisableMacroExpansionAtStartOfDirective; @@ -903,11 +904,14 @@ Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) { // We cannot trust the source range from the value because there was a // parse error. Track the range manually -- the end of the directive is the // end of the condition range. - return {false, + return {std::nullopt, + false, DT.IncludedUndefinedIds, {ExprStartLoc, ConditionRange.getEnd()}}; } + EvaluatedDefined = DT.State != DefinedTracker::Unknown; + // If we are at the end of the expression after just parsing a value, there // must be no (unparenthesized) binary operators involved, so we can exit // directly. @@ -919,7 +923,10 @@ Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) { // Restore 'DisableMacroExpansion'. DisableMacroExpansion = DisableMacroExpansionAtStartOfDirective; - return {ResVal.Val != 0, DT.IncludedUndefinedIds, ResVal.getRange()}; + bool IsNonZero = ResVal.Val != 0; + SourceRange ValRange = ResVal.getRange(); + return {std::move(ResVal.Val), IsNonZero, DT.IncludedUndefinedIds, + ValRange}; } // Otherwise, we must have a binary operator (e.g. "#if 1 < 2"), so parse the @@ -928,21 +935,37 @@ Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) { Tok, true, DT.IncludedUndefinedIds, *this)) { // Parse error, skip the rest of the macro line. if (Tok.isNot(tok::eod)) - DiscardUntilEndOfDirective(); + DiscardUntilEndOfDirective(Tok); // Restore 'DisableMacroExpansion'. DisableMacroExpansion = DisableMacroExpansionAtStartOfDirective; - return {false, DT.IncludedUndefinedIds, ResVal.getRange()}; + SourceRange ValRange = ResVal.getRange(); + return {std::nullopt, false, DT.IncludedUndefinedIds, ValRange}; } - // If we aren't at the tok::eod token, something bad happened, like an extra - // ')' token. - if (Tok.isNot(tok::eod)) { - Diag(Tok, diag::err_pp_expected_eol); - DiscardUntilEndOfDirective(); + if (CheckForEoD) { + // If we aren't at the tok::eod token, something bad happened, like an extra + // ')' token. + if (Tok.isNot(tok::eod)) { + Diag(Tok, diag::err_pp_expected_eol); + DiscardUntilEndOfDirective(Tok); + } } + EvaluatedDefined = EvaluatedDefined || DT.State != DefinedTracker::Unknown; + // Restore 'DisableMacroExpansion'. DisableMacroExpansion = DisableMacroExpansionAtStartOfDirective; - return {ResVal.Val != 0, DT.IncludedUndefinedIds, ResVal.getRange()}; + bool IsNonZero = ResVal.Val != 0; + SourceRange ValRange = ResVal.getRange(); + return {std::move(ResVal.Val), IsNonZero, DT.IncludedUndefinedIds, ValRange}; +} + +Preprocessor::DirectiveEvalResult +Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro, + bool CheckForEoD) { + Token Tok; + bool EvaluatedDefined; + return EvaluateDirectiveExpression(IfNDefMacro, Tok, EvaluatedDefined, + CheckForEoD); } diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index f085b94..3913ff0 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -380,6 +380,7 @@ void Preprocessor::RegisterBuiltinMacros() { Ident__has_c_attribute = nullptr; Ident__has_declspec = RegisterBuiltinMacro(*this, "__has_declspec_attribute"); + Ident__has_embed = RegisterBuiltinMacro(*this, "__has_embed"); Ident__has_include = RegisterBuiltinMacro(*this, "__has_include"); Ident__has_include_next = RegisterBuiltinMacro(*this, "__has_include_next"); Ident__has_warning = RegisterBuiltinMacro(*this, "__has_warning"); @@ -1279,6 +1280,105 @@ static bool EvaluateHasIncludeCommon(Token &Tok, IdentifierInfo *II, return File.has_value(); } +/// EvaluateHasEmbed - Process a '__has_embed("foo" params...)' expression. +/// Returns a filled optional with the value if successful; otherwise, empty. +EmbedResult Preprocessor::EvaluateHasEmbed(Token &Tok, IdentifierInfo *II) { + // These expressions are only allowed within a preprocessor directive. + if (!this->isParsingIfOrElifDirective()) { + Diag(Tok, diag::err_pp_directive_required) << II; + // Return a valid identifier token. + assert(Tok.is(tok::identifier)); + Tok.setIdentifierInfo(II); + return EmbedResult::Invalid; + } + + // Ensure we have a '('. + LexUnexpandedToken(Tok); + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_pp_expected_after) << II << tok::l_paren; + // If the next token looks like a filename or the start of one, + // assume it is and process it as such. + return EmbedResult::Invalid; + } + + // Save '(' location for possible missing ')' message and then lex the header + // name token for the embed resource. + SourceLocation LParenLoc = Tok.getLocation(); + if (this->LexHeaderName(Tok)) + return EmbedResult::Invalid; + + if (Tok.isNot(tok::header_name)) { + Diag(Tok.getLocation(), diag::err_pp_expects_filename); + return EmbedResult::Invalid; + } + + SourceLocation FilenameLoc = Tok.getLocation(); + Token FilenameTok = Tok; + + std::optional<LexEmbedParametersResult> Params = + this->LexEmbedParameters(Tok, /*ForHasEmbed=*/true); + assert((Params || Tok.is(tok::eod)) && + "expected success or to be at the end of the directive"); + + if (!Params) + return EmbedResult::Invalid; + + if (Params->UnrecognizedParams > 0) + return EmbedResult::NotFound; + + if (!Tok.is(tok::r_paren)) { + Diag(this->getLocForEndOfToken(FilenameLoc), diag::err_pp_expected_after) + << II << tok::r_paren; + Diag(LParenLoc, diag::note_matching) << tok::l_paren; + if (Tok.isNot(tok::eod)) + DiscardUntilEndOfDirective(); + return EmbedResult::Invalid; + } + + SmallString<128> FilenameBuffer; + StringRef Filename = this->getSpelling(FilenameTok, FilenameBuffer); + bool isAngled = + this->GetIncludeFilenameSpelling(FilenameTok.getLocation(), Filename); + // If GetIncludeFilenameSpelling set the start ptr to null, there was an + // error. + assert(!Filename.empty()); + const FileEntry *LookupFromFile = + this->getCurrentFileLexer() ? *this->getCurrentFileLexer()->getFileEntry() + : static_cast<FileEntry *>(nullptr); + OptionalFileEntryRef MaybeFileEntry = + this->LookupEmbedFile(Filename, isAngled, false, LookupFromFile); + if (Callbacks) { + Callbacks->HasEmbed(LParenLoc, Filename, isAngled, MaybeFileEntry); + } + if (!MaybeFileEntry) + return EmbedResult::NotFound; + + size_t FileSize = MaybeFileEntry->getSize(); + // First, "offset" into the file (this reduces the amount of data we can read + // from the file). + if (Params->MaybeOffsetParam) { + if (Params->MaybeOffsetParam->Offset > FileSize) + FileSize = 0; + else + FileSize -= Params->MaybeOffsetParam->Offset; + } + + // Second, limit the data from the file (this also reduces the amount of data + // we can read from the file). + if (Params->MaybeLimitParam) { + if (Params->MaybeLimitParam->Limit > FileSize) + FileSize = 0; + else + FileSize = Params->MaybeLimitParam->Limit; + } + + // If we have no data left to read, the file is empty, otherwise we have the + // expected resource. + if (FileSize == 0) + return EmbedResult::Empty; + return EmbedResult::Found; +} + bool Preprocessor::EvaluateHasInclude(Token &Tok, IdentifierInfo *II) { return EvaluateHasIncludeCommon(Tok, II, *this, nullptr, nullptr); } @@ -1820,6 +1920,17 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { return; OS << (int)Value; Tok.setKind(tok::numeric_constant); + } else if (II == Ident__has_embed) { + // The argument to these two builtins should be a parenthesized + // file name string literal using angle brackets (<>) or + // double-quotes (""), optionally followed by a series of + // arguments similar to form like attributes. + EmbedResult Value = EvaluateHasEmbed(Tok, II); + if (Value == EmbedResult::Invalid) + return; + + Tok.setKind(tok::numeric_constant); + OS << static_cast<int>(Value); } else if (II == Ident__has_warning) { // The argument should be a parenthesized string literal. EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false, diff --git a/clang/lib/Lex/TokenConcatenation.cpp b/clang/lib/Lex/TokenConcatenation.cpp index 1b3201b..865879d 100644 --- a/clang/lib/Lex/TokenConcatenation.cpp +++ b/clang/lib/Lex/TokenConcatenation.cpp @@ -193,9 +193,12 @@ bool TokenConcatenation::AvoidConcat(const Token &PrevPrevTok, if (Tok.isAnnotation()) { // Modules annotation can show up when generated automatically for includes. assert(Tok.isOneOf(tok::annot_module_include, tok::annot_module_begin, - tok::annot_module_end) && + tok::annot_module_end, tok::annot_embed) && "unexpected annotation in AvoidConcat"); + ConcatInfo = 0; + if (Tok.is(tok::annot_embed)) + return true; } if (ConcatInfo == 0) diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index eb7447f..9fc3cd7 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1066,6 +1066,21 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, break; } + case tok::annot_embed: { + // We've met #embed in a context where a single value is expected. Take last + // element from #embed data as if it were a comma expression. + EmbedAnnotationData *Data = + reinterpret_cast<EmbedAnnotationData *>(Tok.getAnnotationValue()); + SourceLocation StartLoc = ConsumeAnnotationToken(); + ASTContext &Context = Actions.getASTContext(); + Res = IntegerLiteral::Create(Context, + llvm::APInt(CHAR_BIT, Data->BinaryData.back()), + Context.UnsignedCharTy, StartLoc); + if (Data->BinaryData.size() > 1) + Diag(StartLoc, diag::warn_unused_comma_left_operand); + break; + } + case tok::kw___super: case tok::kw_decltype: // Annotate the token and tail recurse. @@ -3563,6 +3578,17 @@ ExprResult Parser::ParseFoldExpression(ExprResult LHS, T.getCloseLocation()); } +void Parser::ExpandEmbedDirective(SmallVectorImpl<Expr *> &Exprs) { + EmbedAnnotationData *Data = + reinterpret_cast<EmbedAnnotationData *>(Tok.getAnnotationValue()); + SourceLocation StartLoc = ConsumeAnnotationToken(); + ASTContext &Context = Actions.getASTContext(); + for (auto Byte : Data->BinaryData) { + Exprs.push_back(IntegerLiteral::Create(Context, llvm::APInt(CHAR_BIT, Byte), + Context.UnsignedCharTy, StartLoc)); + } +} + /// ParseExpressionList - Used for C/C++ (argument-)expression-list. /// /// \verbatim @@ -3598,8 +3624,17 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs, if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); Expr = ParseBraceInitializer(); - } else + } else if (Tok.is(tok::annot_embed)) { + ExpandEmbedDirective(Exprs); + if (Tok.isNot(tok::comma)) + break; + Token Comma = Tok; + ConsumeToken(); + checkPotentialAngleBracketDelimiter(Comma); + continue; + } else { Expr = ParseAssignmentExpression(); + } if (EarlyTypoCorrection) Expr = Actions.CorrectDelayedTyposInExpr(Expr); diff --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp index 432ddc7..cd11f90 100644 --- a/clang/lib/Parse/ParseInit.cpp +++ b/clang/lib/Parse/ParseInit.cpp @@ -428,6 +428,36 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator( return ExprError(); } +ExprResult Parser::createEmbedExpr() { + assert(Tok.getKind() == tok::annot_embed); + EmbedAnnotationData *Data = + reinterpret_cast<EmbedAnnotationData *>(Tok.getAnnotationValue()); + ExprResult Res; + ASTContext &Context = Actions.getASTContext(); + SourceLocation StartLoc = ConsumeAnnotationToken(); + if (Data->BinaryData.size() == 1) { + Res = IntegerLiteral::Create(Context, + llvm::APInt(CHAR_BIT, Data->BinaryData.back()), + Context.UnsignedCharTy, StartLoc); + } else { + auto CreateStringLiteralFromStringRef = [&](StringRef Str, QualType Ty) { + llvm::APSInt ArraySize = + Context.MakeIntValue(Str.size(), Context.getSizeType()); + QualType ArrayTy = Context.getConstantArrayType( + Ty, ArraySize, nullptr, ArraySizeModifier::Normal, 0); + return StringLiteral::Create(Context, Str, StringLiteralKind::Ordinary, + false, ArrayTy, StartLoc); + }; + + StringLiteral *FileNameArg = + CreateStringLiteralFromStringRef(Data->FileName, Context.CharTy); + StringLiteral *BinaryDataArg = CreateStringLiteralFromStringRef( + Data->BinaryData, Context.UnsignedCharTy); + Res = Actions.ActOnEmbedExpr(StartLoc, FileNameArg, BinaryDataArg); + } + return Res; +} + /// ParseBraceInitializer - Called when parsing an initializer that has a /// leading open brace. /// @@ -501,6 +531,8 @@ ExprResult Parser::ParseBraceInitializer() { ExprResult SubElt; if (MayBeDesignationStart()) SubElt = ParseInitializerWithPotentialDesignator(DesignatorCompletion); + else if (Tok.getKind() == tok::annot_embed) + SubElt = createEmbedExpr(); else SubElt = ParseInitializer(); diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index a5130f5..7e30afa 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -1523,6 +1523,19 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() { ExprArg.get(), Loc); } +void Parser::ExpandEmbedIntoTemplateArgList(TemplateArgList &TemplateArgs) { + EmbedAnnotationData *Data = + reinterpret_cast<EmbedAnnotationData *>(Tok.getAnnotationValue()); + SourceLocation StartLoc = ConsumeAnnotationToken(); + ASTContext &Context = Actions.getASTContext(); + for (auto Byte : Data->BinaryData) { + Expr *E = IntegerLiteral::Create(Context, llvm::APInt(CHAR_BIT, Byte), + Context.UnsignedCharTy, StartLoc); + TemplateArgs.push_back( + ParsedTemplateArgument(ParsedTemplateArgument::NonType, E, StartLoc)); + } +} + /// ParseTemplateArgumentList - Parse a C++ template-argument-list /// (C++ [temp.names]). Returns true if there was an error. /// @@ -1547,19 +1560,23 @@ bool Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs, do { PreferredType.enterFunctionArgument(Tok.getLocation(), RunSignatureHelp); - ParsedTemplateArgument Arg = ParseTemplateArgument(); - SourceLocation EllipsisLoc; - if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) - Arg = Actions.ActOnPackExpansion(Arg, EllipsisLoc); - - if (Arg.isInvalid()) { - if (PP.isCodeCompletionReached() && !CalledSignatureHelp) - RunSignatureHelp(); - return true; - } + if (Tok.is(tok::annot_embed)) { + ExpandEmbedIntoTemplateArgList(TemplateArgs); + } else { + ParsedTemplateArgument Arg = ParseTemplateArgument(); + SourceLocation EllipsisLoc; + if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) + Arg = Actions.ActOnPackExpansion(Arg, EllipsisLoc); + + if (Arg.isInvalid()) { + if (PP.isCodeCompletionReached() && !CalledSignatureHelp) + RunSignatureHelp(); + return true; + } - // Save this template argument. - TemplateArgs.push_back(Arg); + // Save this template argument. + TemplateArgs.push_back(Arg); + } // If the next token is a comma, consume it and keep reading // arguments. diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 17acfca..0febfa8 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1414,6 +1414,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::PackIndexingExprClass: case Expr::StringLiteralClass: case Expr::SourceLocExprClass: + case Expr::EmbedExprClass: 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 76145f2..44f886b 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3711,7 +3711,7 @@ bool Sema::CheckLoopHintExpr(Expr *E, SourceLocation Loc, bool AllowZero) { bool ValueIsPositive = AllowZero ? ValueAPS.isNonNegative() : ValueAPS.isStrictlyPositive(); if (!ValueIsPositive || ValueAPS.getActiveBits() > 31) { - Diag(E->getExprLoc(), diag::err_pragma_loop_invalid_argument_value) + Diag(E->getExprLoc(), diag::err_requires_positive_value) << toString(ValueAPS, 10) << ValueIsPositive; return true; } @@ -7290,8 +7290,8 @@ Sema::BuildInitList(SourceLocation LBraceLoc, MultiExprArg InitArgList, } } - InitListExpr *E = new (Context) InitListExpr(Context, LBraceLoc, InitArgList, - RBraceLoc); + InitListExpr *E = + new (Context) InitListExpr(Context, LBraceLoc, InitArgList, RBraceLoc); E->setType(Context.VoidTy); // FIXME: just a place holder for now. return E; } @@ -16679,6 +16679,17 @@ ExprResult Sema::BuildSourceLocExpr(SourceLocIdentKind Kind, QualType ResultTy, SourceLocExpr(Context, Kind, ResultTy, BuiltinLoc, RPLoc, ParentContext); } +ExprResult Sema::ActOnEmbedExpr(SourceLocation EmbedKeywordLoc, + StringLiteral *Filename, + StringLiteral *BinaryData) { + EmbedDataStorage *Data = new (Context) EmbedDataStorage; + Data->Filename = Filename; + Data->BinaryData = BinaryData; + return new (Context) + EmbedExpr(Context, EmbedKeywordLoc, Data, /*NumOfElements=*/0, + Data->getDataElementCount()); +} + static bool maybeDiagnoseAssignmentToFunction(Sema &S, QualType DstType, const Expr *SrcExpr) { if (!DstType->isFunctionPointerType() || diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 7244f3e..4f2a46d 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -313,6 +313,8 @@ class InitListChecker { InitListExpr *FullyStructuredList = nullptr; NoInitExpr *DummyExpr = nullptr; SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr; + EmbedExpr *CurEmbed = nullptr; // Save current embed we're processing. + unsigned CurEmbedIndex = 0; NoInitExpr *getDummyInit() { if (!DummyExpr) @@ -501,6 +503,42 @@ class InitListChecker { void CheckEmptyInitializable(const InitializedEntity &Entity, SourceLocation Loc); + Expr *HandleEmbed(EmbedExpr *Embed, const InitializedEntity &Entity) { + Expr *Result = nullptr; + // Undrestand which part of embed we'd like to reference. + if (!CurEmbed) { + CurEmbed = Embed; + CurEmbedIndex = 0; + } + // Reference just one if we're initializing a single scalar. + uint64_t ElsCount = 1; + // Otherwise try to fill whole array with embed data. + if (Entity.getKind() == InitializedEntity::EK_ArrayElement) { + ValueDecl *ArrDecl = Entity.getParent()->getDecl(); + auto *AType = SemaRef.Context.getAsArrayType(ArrDecl->getType()); + assert(AType && "expected array type when initializing array"); + ElsCount = Embed->getDataElementCount(); + if (const auto *CAType = dyn_cast<ConstantArrayType>(AType)) + ElsCount = std::min(CAType->getSize().getZExtValue(), + ElsCount - CurEmbedIndex); + if (ElsCount == Embed->getDataElementCount()) { + CurEmbed = nullptr; + CurEmbedIndex = 0; + return Embed; + } + } + + Result = new (SemaRef.Context) + EmbedExpr(SemaRef.Context, Embed->getLocation(), Embed->getData(), + CurEmbedIndex, ElsCount); + CurEmbedIndex += ElsCount; + if (CurEmbedIndex >= Embed->getDataElementCount()) { + CurEmbed = nullptr; + CurEmbedIndex = 0; + } + return Result; + } + public: InitListChecker( Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T, @@ -1459,6 +1497,9 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, // Brace elision is never performed if the element is not an // assignment-expression. if (Seq || isa<InitListExpr>(expr)) { + if (auto *Embed = dyn_cast<EmbedExpr>(expr)) { + expr = HandleEmbed(Embed, Entity); + } if (!VerifyOnly) { ExprResult Result = Seq.Perform(SemaRef, TmpEntity, Kind, expr); if (Result.isInvalid()) @@ -1472,7 +1513,8 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, UpdateStructuredListElement(StructuredList, StructuredIndex, getDummyInit()); } - ++Index; + if (!CurEmbed) + ++Index; if (AggrDeductionCandidateParamTypes) AggrDeductionCandidateParamTypes->push_back(ElemType); return; @@ -1665,6 +1707,8 @@ void InitListChecker::CheckScalarType(const InitializedEntity &Entity, ++Index; ++StructuredIndex; return; + } else if (auto *Embed = dyn_cast<EmbedExpr>(expr)) { + expr = HandleEmbed(Embed, Entity); } ExprResult Result; @@ -1686,14 +1730,16 @@ void InitListChecker::CheckScalarType(const InitializedEntity &Entity, else { ResultExpr = Result.getAs<Expr>(); - if (ResultExpr != expr && !VerifyOnly) { + if (ResultExpr != expr && !VerifyOnly && !CurEmbed) { // The type was promoted, update initializer list. // FIXME: Why are we updating the syntactic init list? IList->setInit(Index, ResultExpr); } } + UpdateStructuredListElement(StructuredList, StructuredIndex, ResultExpr); - ++Index; + if (!CurEmbed) + ++Index; if (AggrDeductionCandidateParamTypes) AggrDeductionCandidateParamTypes->push_back(DeclType); } @@ -1932,6 +1978,30 @@ static bool checkDestructorReference(QualType ElementType, SourceLocation Loc, return SemaRef.DiagnoseUseOfDecl(Destructor, Loc); } +static bool canInitializeArrayWithEmbedDataString(ArrayRef<Expr *> ExprList, + QualType InitType, + ASTContext &Context) { + // Only one initializer, it's an embed and the types match; + EmbedExpr *EE = + ExprList.size() == 1 + ? dyn_cast_if_present<EmbedExpr>(ExprList[0]->IgnoreParens()) + : nullptr; + if (!EE) + return false; + + if (InitType->isArrayType()) { + const ArrayType *InitArrayType = InitType->getAsArrayTypeUnsafe(); + QualType InitElementTy = InitArrayType->getElementType(); + QualType EmbedExprElementTy = EE->getType(); + const bool TypesMatch = + Context.typesAreCompatible(InitElementTy, EmbedExprElementTy) || + (InitElementTy->isCharType() && EmbedExprElementTy->isCharType()); + if (TypesMatch) + return true; + } + return false; +} + void InitListChecker::CheckArrayType(const InitializedEntity &Entity, InitListExpr *IList, QualType &DeclType, llvm::APSInt elementIndex, @@ -1949,6 +2019,12 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity, } } + if (canInitializeArrayWithEmbedDataString(IList->inits(), DeclType, + SemaRef.Context)) { + EmbedExpr *Embed = cast<EmbedExpr>(IList->inits()[0]); + IList->setInit(0, Embed->getDataStringLiteral()); + } + // Check for the special-case of initializing an array with a string. if (Index < IList->getNumInits()) { if (IsStringInit(IList->getInit(Index), arrayType, SemaRef.Context) == @@ -2051,13 +2127,24 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity, if (maxElementsKnown && elementIndex == maxElements) break; - InitializedEntity ElementEntity = - InitializedEntity::InitializeElement(SemaRef.Context, StructuredIndex, - Entity); + InitializedEntity ElementEntity = InitializedEntity::InitializeElement( + SemaRef.Context, StructuredIndex, Entity); + + unsigned EmbedElementIndexBeforeInit = CurEmbedIndex; // Check this element. CheckSubElementType(ElementEntity, IList, elementType, Index, StructuredList, StructuredIndex); ++elementIndex; + if ((CurEmbed || isa<EmbedExpr>(Init)) && elementType->isScalarType()) { + if (CurEmbed) { + elementIndex = + elementIndex + CurEmbedIndex - EmbedElementIndexBeforeInit - 1; + } else { + auto Embed = cast<EmbedExpr>(Init); + elementIndex = elementIndex + Embed->getDataElementCount() - + EmbedElementIndexBeforeInit - 1; + } + } // If the array is of incomplete type, keep track of the number of // elements in the initializer. @@ -9063,19 +9150,18 @@ ExprResult InitializationSequence::Perform(Sema &S, } } } - + Expr *Init = CurInit.get(); CheckedConversionKind CCK = Kind.isCStyleCast() ? CheckedConversionKind::CStyleCast : Kind.isFunctionalCast() ? CheckedConversionKind::FunctionalCast : Kind.isExplicitCast() ? CheckedConversionKind::OtherCast : CheckedConversionKind::Implicit; - ExprResult CurInitExprRes = - S.PerformImplicitConversion(CurInit.get(), Step->Type, *Step->ICS, - getAssignmentAction(Entity), CCK); + ExprResult CurInitExprRes = S.PerformImplicitConversion( + Init, Step->Type, *Step->ICS, getAssignmentAction(Entity), CCK); if (CurInitExprRes.isInvalid()) return ExprError(); - S.DiscardMisalignedMemberAddress(Step->Type.getTypePtr(), CurInit.get()); + S.DiscardMisalignedMemberAddress(Step->Type.getTypePtr(), Init); CurInit = CurInitExprRes; @@ -9230,10 +9316,11 @@ ExprResult InitializationSequence::Perform(Sema &S, case SK_CAssignment: { QualType SourceType = CurInit.get()->getType(); + Expr *Init = CurInit.get(); // Save off the initial CurInit in case we need to emit a diagnostic - ExprResult InitialCurInit = CurInit; - ExprResult Result = CurInit; + ExprResult InitialCurInit = Init; + ExprResult Result = Init; Sema::AssignConvertType ConvTy = S.CheckSingleAssignmentConstraints(Step->Type, Result, true, Entity.getKind() == InitializedEntity::EK_Parameter_CF_Audited); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 3bfda09..f117fe9 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -12939,6 +12939,11 @@ ExprResult TreeTransform<Derived>::TransformSourceLocExpr(SourceLocExpr *E) { getSema().CurContext); } +template <typename Derived> +ExprResult TreeTransform<Derived>::TransformEmbedExpr(EmbedExpr *E) { + return E; +} + template<typename Derived> ExprResult TreeTransform<Derived>::TransformCUDAKernelCallExpr(CUDAKernelCallExpr *E) { diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 67ef170..a0ffe24 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1323,6 +1323,17 @@ void ASTStmtReader::VisitSourceLocExpr(SourceLocExpr *E) { E->SourceLocExprBits.Kind = Record.readInt(); } +void ASTStmtReader::VisitEmbedExpr(EmbedExpr *E) { + VisitExpr(E); + E->EmbedKeywordLoc = readSourceLocation(); + EmbedDataStorage *Data = new (Record.getContext()) EmbedDataStorage; + Data->Filename = cast<StringLiteral>(Record.readSubStmt()); + Data->BinaryData = cast<StringLiteral>(Record.readSubStmt()); + E->Data = Data; + E->Begin = Record.readInt(); + E->NumOfElements = Record.readInt(); +} + void ASTStmtReader::VisitAddrLabelExpr(AddrLabelExpr *E) { VisitExpr(E); E->setAmpAmpLoc(readSourceLocation()); @@ -3233,6 +3244,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) SourceLocExpr(Empty); break; + case EXPR_BUILTIN_PP_EMBED: + S = new (Context) EmbedExpr(Empty); + break; + case EXPR_ADDR_LABEL: S = new (Context) AddrLabelExpr(Empty); break; diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 1a98e30..ed2145e 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1262,6 +1262,17 @@ void ASTStmtWriter::VisitSourceLocExpr(SourceLocExpr *E) { Code = serialization::EXPR_SOURCE_LOC; } +void ASTStmtWriter::VisitEmbedExpr(EmbedExpr *E) { + VisitExpr(E); + Record.AddSourceLocation(E->getBeginLoc()); + Record.AddSourceLocation(E->getEndLoc()); + Record.AddStmt(E->getFilenameStringLiteral()); + Record.AddStmt(E->getDataStringLiteral()); + Record.writeUInt32(E->getStartingElementPos()); + Record.writeUInt32(E->getDataElementCount()); + Code = serialization::EXPR_BUILTIN_PP_EMBED; +} + void ASTStmtWriter::VisitAddrLabelExpr(AddrLabelExpr *E) { VisitExpr(E); Record.AddSourceLocation(E->getAmpAmpLoc()); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 197d673..b331be8 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2422,6 +2422,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; } + + case Stmt::EmbedExprClass: + llvm_unreachable("Support for EmbedExpr is not implemented."); + break; } } |