https://github.com/StoeckOverflow updated https://github.com/llvm/llvm-project/pull/216148
>From 42b6514b148da69dbd814eeeba9cb8e1742063fb Mon Sep 17 00:00:00 2001 From: stoeckoverflow <[email protected]> Date: Tue, 11 Aug 2026 11:54:31 +0200 Subject: [PATCH 1/2] [APINotes] Add Where.Object selectors for C++ methods --- clang/include/clang/APINotes/APINotesReader.h | 20 ++- clang/include/clang/APINotes/APINotesWriter.h | 6 + clang/include/clang/APINotes/Types.h | 150 ++++++++++++++++-- clang/lib/APINotes/APINotesFormat.h | 62 +++++++- clang/lib/APINotes/APINotesReader.cpp | 122 +++++++++++--- clang/lib/APINotes/APINotesWriter.cpp | 77 ++++++++- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 132 ++++++++++----- clang/lib/Sema/SemaAPINotes.cpp | 141 +++++++++++++--- clang/lib/Sema/SemaAPINotesInternal.h | 8 +- .../Headers/WhereObjectQualifiers.apinotes | 96 +++++++++++ .../Inputs/Headers/WhereObjectQualifiers.h | 30 ++++ .../APINotes/Inputs/Headers/module.modulemap | 5 + .../APINotes.apinotes | 46 ++++++ .../WhereObjectQualifiers.h | 14 ++ .../test/APINotes/where-object-qualifiers.cpp | 63 ++++++++ 15 files changed, 865 insertions(+), 107 deletions(-) create mode 100644 clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.apinotes create mode 100644 clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.h create mode 100644 clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/APINotes.apinotes create mode 100644 clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/WhereObjectQualifiers.h create mode 100644 clang/test/APINotes/where-object-qualifiers.cpp diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index d74232bc334c6..99303c8a8ba02 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -170,6 +170,12 @@ class APINotesReader { lookupCXXMethod(ContextID CtxID, llvm::StringRef Name, llvm::ArrayRef<std::string> Parameters); + /// Look for information regarding the given C++ method with a composed + /// selector. Omitted selector components use the broad name-based key. + VersionedInfo<CXXMethodInfo> + lookupCXXMethod(ContextID CtxID, llvm::StringRef Name, + const FunctionSelector &Selector); + /// Build the selector key for the given C++ method. std::optional<APINotesFunctionSelectorKey> getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name); @@ -180,6 +186,12 @@ class APINotesReader { getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name, llvm::ArrayRef<std::string> Parameters); + /// Build the selector key for the given C++ method with a composed + /// selector. + std::optional<APINotesFunctionSelectorKey> + getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name, + const FunctionSelector &Selector); + /// Look for information regarding the given global variable. /// /// \param Name The name of the global variable. @@ -218,8 +230,9 @@ class APINotesReader { llvm::ArrayRef<std::string> Parameters, std::optional<Context> Ctx = std::nullopt); - /// Collect exact parameter selector keys stored by this reader. - void collectExactFunctionParameterSelectors( + /// Collect selector keys stored by this reader that should be diagnosed if + /// unmatched. + void collectFunctionSelectorsForDiagnostics( llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors); /// Reconstruct parameter selector strings for a stored exact selector key. @@ -275,10 +288,9 @@ class APINotesReader { private: VersionedInfo<CXXMethodInfo> lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name); - template <typename ParameterT> VersionedInfo<CXXMethodInfo> lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name, - llvm::ArrayRef<ParameterT> Parameters); + const FunctionSelector &Selector); VersionedInfo<GlobalFunctionInfo> lookupGlobalFunctionImpl(llvm::StringRef Name, std::optional<Context> Ctx); diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h index 5ed6686e1bb85..c0d07ceeb30cc 100644 --- a/clang/include/clang/APINotes/APINotesWriter.h +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -95,6 +95,12 @@ class APINotesWriter { llvm::ArrayRef<llvm::StringRef> Parameters, const CXXMethodInfo &Info, llvm::VersionTuple SwiftVersion); + /// Add information about a C++ method with a composed selector. Omitted + /// selector components use the broad name-based key. + void addCXXMethod(ContextID CtxID, llvm::StringRef Name, + const FunctionSelector &Selector, const CXXMethodInfo &Info, + llvm::VersionTuple SwiftVersion); + /// Add information about a specific C record field. /// /// \param CtxID The context in which this field resides, i.e. a C/C++ tag. diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index af989d3a1b7f0..4151e26a98b45 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -1008,19 +1008,132 @@ struct Context { using IdentifierID = llvm::PointerEmbeddedInt<unsigned, 31>; +/// Describes the C++ implicit-object ref-qualifier portion of a method +/// selector. +enum class FunctionObjectRefQualifier : uint8_t { + None, + LValue, + RValue, +}; + +/// Describes optional constraints on a C++ method's implicit object parameter. +struct FunctionObjectSelector { + std::optional<bool> Const; + std::optional<bool> Volatile; + std::optional<FunctionObjectRefQualifier> Ref; +}; + +inline bool operator==(const FunctionObjectSelector &LHS, + const FunctionObjectSelector &RHS) { + return LHS.Const == RHS.Const && LHS.Volatile == RHS.Volatile && + LHS.Ref == RHS.Ref; +} + +inline bool operator!=(const FunctionObjectSelector &LHS, + const FunctionObjectSelector &RHS) { + return !(LHS == RHS); +} + +inline std::string formatAPINotesObjectSelector(FunctionObjectSelector Object) { + std::string Result; + llvm::raw_string_ostream OS(Result); + llvm::SmallVector<std::string, 3> Parts; + + if (Object.Const) + Parts.push_back(std::string("Const: ") + + (*Object.Const ? "true" : "false")); + if (Object.Volatile) + Parts.push_back(std::string("Volatile: ") + + (*Object.Volatile ? "true" : "false")); + if (Object.Ref) { + std::string Ref = "Ref: "; + switch (*Object.Ref) { + case FunctionObjectRefQualifier::None: + Ref += "none"; + break; + case FunctionObjectRefQualifier::LValue: + Ref += "lvalue"; + break; + case FunctionObjectRefQualifier::RValue: + Ref += "rvalue"; + break; + } + Parts.push_back(Ref); + } + + OS << "Object{"; + llvm::interleaveComma(Parts, OS); + OS << "}"; + return Result; +} + +/// Describes a C++ function selector composed from optional exact explicit +/// parameters plus optional implicit object constraints. +struct FunctionSelector { + std::optional<llvm::SmallVector<std::string, 4>> Parameters; + std::optional<FunctionObjectSelector> Object; +}; + +inline bool operator==(const FunctionSelector &LHS, + const FunctionSelector &RHS) { + return LHS.Parameters == RHS.Parameters && LHS.Object == RHS.Object; +} + +inline bool operator!=(const FunctionSelector &LHS, + const FunctionSelector &RHS) { + return !(LHS == RHS); +} + +inline std::string formatAPINotesFunctionSelector( + std::optional<llvm::ArrayRef<std::string>> Parameters, + std::optional<FunctionObjectSelector> Object) { + std::string Result; + if (Parameters) + Result = (llvm::Twine("Where.Parameters ") + + formatAPINotesParameterSelector(*Parameters)) + .str(); + + if (Object) { + if (!Result.empty()) + Result += " "; + else + Result = "Where.Object "; + Result += formatAPINotesObjectSelector(*Object); + } + + return Result; +} + +inline std::string +formatAPINotesFunctionSelector(const FunctionSelector &Selector) { + std::optional<llvm::ArrayRef<std::string>> Parameters; + if (Selector.Parameters) + Parameters = llvm::ArrayRef<std::string>(*Selector.Parameters); + return formatAPINotesFunctionSelector(Parameters, Selector.Object); +} + +struct FunctionTableSelectorKey { + std::optional<llvm::SmallVector<IdentifierID, 2>> Parameters; + std::optional<FunctionObjectSelector> Object; +}; + /// A key for a stored global-function or C++-method API notes entry. /// /// The key is represented by the ID of its parent context, the declaration -/// name, and optional exact parameter types. +/// name, and optional exact parameter/object selector data. struct FunctionTableKey { uint32_t parentContextID; uint32_t nameID; std::optional<llvm::SmallVector<IdentifierID, 2>> parameterTypeIDs; + std::optional<FunctionObjectSelector> objectSelector; FunctionTableKey() : parentContextID(-1), nameID(-1) {} - FunctionTableKey(uint32_t ParentContextID, uint32_t NameID) - : parentContextID(ParentContextID), nameID(NameID) {} + FunctionTableKey(uint32_t ParentContextID, uint32_t NameID, + FunctionTableSelectorKey Selector = {}) + : parentContextID(ParentContextID), nameID(NameID), + parameterTypeIDs(std::move(Selector.Parameters)), + objectSelector(Selector.Object) {} FunctionTableKey(uint32_t ParentContextID, uint32_t NameID, const llvm::SmallVectorImpl<IdentifierID> &ParameterTypeIDs) @@ -1028,27 +1141,37 @@ struct FunctionTableKey { parameterTypeIDs.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end()); } - FunctionTableKey(std::optional<Context> ParentCtx, IdentifierID NameID) - : parentContextID(ParentCtx ? ParentCtx->id.Value - : static_cast<uint32_t>(-1)), - nameID(NameID) {} + FunctionTableKey(std::optional<Context> ParentCtx, IdentifierID NameID, + FunctionTableSelectorKey Selector = {}) + : FunctionTableKey(ParentCtx ? ParentCtx->id.Value + : static_cast<uint32_t>(-1), + NameID, std::move(Selector)) {} FunctionTableKey(std::optional<Context> ParentCtx, IdentifierID NameID, const llvm::SmallVectorImpl<IdentifierID> &ParameterTypeIDs) - : parentContextID(ParentCtx ? ParentCtx->id.Value - : static_cast<uint32_t>(-1)), - nameID(NameID) { + : FunctionTableKey(ParentCtx ? ParentCtx->id.Value + : static_cast<uint32_t>(-1), + NameID) { parameterTypeIDs.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end()); } llvm::hash_code hashValue() const { auto Hash = llvm::hash_combine(parentContextID, nameID, - static_cast<bool>(parameterTypeIDs)); + static_cast<bool>(parameterTypeIDs), + static_cast<bool>(objectSelector)); if (parameterTypeIDs) { Hash = llvm::hash_combine(Hash, parameterTypeIDs->size()); for (IdentifierID TypeID : *parameterTypeIDs) Hash = llvm::hash_combine(Hash, static_cast<unsigned>(TypeID)); } + if (objectSelector) + Hash = llvm::hash_combine( + Hash, objectSelector->Const.value_or(false), + static_cast<bool>(objectSelector->Const), + objectSelector->Volatile.value_or(false), + static_cast<bool>(objectSelector->Volatile), + objectSelector->Ref ? static_cast<unsigned>(*objectSelector->Ref) + 1 + : 0); return Hash; } }; @@ -1057,7 +1180,8 @@ inline bool operator==(const FunctionTableKey &LHS, const FunctionTableKey &RHS) { return LHS.parentContextID == RHS.parentContextID && LHS.nameID == RHS.nameID && - LHS.parameterTypeIDs == RHS.parameterTypeIDs; + LHS.parameterTypeIDs == RHS.parameterTypeIDs && + LHS.objectSelector == RHS.objectSelector; } /// Stable reader-facing identity for an API notes function selector entry. @@ -1069,7 +1193,7 @@ struct APINotesFunctionSelectorKey { FunctionTableKey Key; bool IsCXXMethod = false; - APINotesFunctionSelectorKey getWithoutParameterSelector() const { + APINotesFunctionSelectorKey getNameOnlyKey() const { return {FunctionTableKey(Key.parentContextID, Key.nameID), IsCXXMethod}; } diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 30fc8599349bf..f0ea371e3ccef 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -16,6 +16,7 @@ #include "llvm/Bitcode/BitcodeConvenience.h" #include <optional> +#include <utility> namespace clang { namespace api_notes { @@ -28,9 +29,10 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 41; // 39 for BoundsSafety; +const uint16_t VERSION_MINOR = 42; // 39 for BoundsSafety; // 40 for UnsafeBufferUsageAttr // 41 for FunctionTableKey parameters + // 42 for FunctionTableKey object selectors const uint8_t kSwiftConforms = 1; const uint8_t kSwiftDoesNotConform = 2; @@ -359,8 +361,21 @@ inline bool operator==(const SingleDeclTableKey &lhs, } /// A stored C or C++ function declaration, represented by the ID of its parent -/// context, the name of the declaration, and optional exact parameter types. +/// context, the name of the declaration, and optional exact parameter/object +/// selector data. constexpr uint8_t FunctionKeyHasParameterSelector = 0x01; +constexpr uint8_t FunctionKeyObjectConstPresent = 0x02; +constexpr uint8_t FunctionKeyObjectConstValue = 0x04; +constexpr uint8_t FunctionKeyObjectVolatilePresent = 0x08; +constexpr uint8_t FunctionKeyObjectVolatileValue = 0x10; +constexpr uint8_t FunctionKeyObjectRefPresent = 0x20; +constexpr uint8_t FunctionKeyObjectRefLValue = 0x40; +constexpr uint8_t FunctionKeyObjectRefRValue = 0x80; +constexpr uint8_t FunctionKeyObjectSelectorMask = + FunctionKeyObjectConstPresent | FunctionKeyObjectConstValue | + FunctionKeyObjectVolatilePresent | FunctionKeyObjectVolatileValue | + FunctionKeyObjectRefPresent | FunctionKeyObjectRefLValue | + FunctionKeyObjectRefRValue; constexpr unsigned FunctionTableKeyBaseLength = sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint16_t); @@ -375,10 +390,48 @@ getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, return FunctionTableKey(ParentContextID, *NameID); } +template <typename GetIdentifierFn> +std::optional<FunctionTableKey> +getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, + FunctionObjectSelector ObjectSelector, + GetIdentifierFn GetIdentifier) { + std::optional<IdentifierID> NameID = GetIdentifier(Name); + if (!NameID) + return std::nullopt; + + FunctionTableSelectorKey Selector; + Selector.Object = ObjectSelector; + return FunctionTableKey(ParentContextID, *NameID, std::move(Selector)); +} + +template <typename ParameterT, typename GetIdentifierFn> +std::optional<FunctionTableKey> +getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, + llvm::ArrayRef<ParameterT> Parameters, + GetIdentifierFn GetIdentifier) { + std::optional<IdentifierID> NameID = GetIdentifier(Name); + if (!NameID) + return std::nullopt; + + llvm::SmallVector<IdentifierID, 2> ParameterTypeIDs; + ParameterTypeIDs.reserve(Parameters.size()); + for (const ParameterT &Parameter : Parameters) { + std::optional<IdentifierID> ParameterID = + GetIdentifier(llvm::StringRef(Parameter)); + if (!ParameterID) + return std::nullopt; + ParameterTypeIDs.push_back(*ParameterID); + } + FunctionTableSelectorKey Selector; + Selector.Parameters.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end()); + return FunctionTableKey(ParentContextID, *NameID, std::move(Selector)); +} + template <typename ParameterT, typename GetIdentifierFn> std::optional<FunctionTableKey> getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, llvm::ArrayRef<ParameterT> Parameters, + FunctionObjectSelector ObjectSelector, GetIdentifierFn GetIdentifier) { std::optional<IdentifierID> NameID = GetIdentifier(Name); if (!NameID) @@ -393,7 +446,10 @@ getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, return std::nullopt; ParameterTypeIDs.push_back(*ParameterID); } - return FunctionTableKey(ParentContextID, *NameID, ParameterTypeIDs); + FunctionTableSelectorKey Selector; + Selector.Parameters.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end()); + Selector.Object = ObjectSelector; + return FunctionTableKey(ParentContextID, *NameID, std::move(Selector)); } } // namespace api_notes diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index aad597d93d9ee..ab06d75826d1a 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -72,14 +72,47 @@ static FunctionTableKey readFunctionTableKey(const uint8_t *Data, ParameterTypeIDs.push_back( endian::readNext<uint32_t, llvm::endianness::little>(Data)); - assert((FunctionKeyFlags & ~FunctionKeyHasParameterSelector) == 0 && - "Unexpected function table key flags"); + if (FunctionKeyFlags & FunctionKeyObjectRefLValue) + assert(!(FunctionKeyFlags & FunctionKeyObjectRefRValue) && + "Unexpected function table key ref qualifier flags"); + assert(((FunctionKeyFlags & FunctionKeyObjectConstValue) == 0 || + (FunctionKeyFlags & FunctionKeyObjectConstPresent)) && + "Function table key const value requires presence flag"); + assert(((FunctionKeyFlags & FunctionKeyObjectVolatileValue) == 0 || + (FunctionKeyFlags & FunctionKeyObjectVolatilePresent)) && + "Function table key volatile value requires presence flag"); + assert(((FunctionKeyFlags & + (FunctionKeyObjectRefLValue | FunctionKeyObjectRefRValue)) == 0 || + (FunctionKeyFlags & FunctionKeyObjectRefPresent)) && + "Function table key ref value requires presence flag"); + std::optional<FunctionObjectSelector> ObjectSelector; + if (FunctionKeyFlags & FunctionKeyObjectSelectorMask) { + FunctionObjectSelector Selector; + if (FunctionKeyFlags & FunctionKeyObjectConstPresent) + Selector.Const = (FunctionKeyFlags & FunctionKeyObjectConstValue) != 0; + if (FunctionKeyFlags & FunctionKeyObjectVolatilePresent) + Selector.Volatile = + (FunctionKeyFlags & FunctionKeyObjectVolatileValue) != 0; + if (FunctionKeyFlags & FunctionKeyObjectRefPresent) { + if (FunctionKeyFlags & FunctionKeyObjectRefLValue) + Selector.Ref = FunctionObjectRefQualifier::LValue; + else if (FunctionKeyFlags & FunctionKeyObjectRefRValue) + Selector.Ref = FunctionObjectRefQualifier::RValue; + else + Selector.Ref = FunctionObjectRefQualifier::None; + } + ObjectSelector = Selector; + } + + FunctionTableSelectorKey Selector; if (FunctionKeyFlags & FunctionKeyHasParameterSelector) - return {CtxID, NameID, ParameterTypeIDs}; - - assert(ParameterTypeIDs.empty() && - "Broad function table key should not store parameters"); - return {CtxID, NameID}; + Selector.Parameters.emplace(ParameterTypeIDs.begin(), + ParameterTypeIDs.end()); + else + assert(ParameterTypeIDs.empty() && + "Broad function table key should not store parameters"); + Selector.Object = ObjectSelector; + return FunctionTableKey(CtxID, NameID, std::move(Selector)); } /// An on-disk hash table whose data is versioned based on the Swift version. @@ -844,10 +877,10 @@ class APINotesReader::Implementation { /// the ID is unknown. std::optional<llvm::StringRef> getIdentifierString(IdentifierID ID); - /// Collect exact parameter selector keys stored in the given function-like - /// table. + /// Collect selector keys stored in the given function-like table that + /// should be diagnosed if unmatched. template <typename TableT> - void collectExactFunctionParameterSelectors( + void collectFunctionSelectorsForDiagnostics( TableT &Table, llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors); @@ -880,11 +913,17 @@ class APINotesReader::Implementation { getFunctionKey(uint32_t ParentContextID, llvm::StringRef Name, llvm::ArrayRef<ParameterT> Parameters); std::optional<FunctionTableKey> + getFunctionKey(uint32_t ParentContextID, llvm::StringRef Name, + const FunctionSelector &Selector); + std::optional<FunctionTableKey> getFunctionKey(std::optional<Context> ParentContext, llvm::StringRef Name); template <typename ParameterT> std::optional<FunctionTableKey> getFunctionKey(std::optional<Context> ParentContext, llvm::StringRef Name, llvm::ArrayRef<ParameterT> Parameters); + std::optional<FunctionTableKey> + getFunctionKey(std::optional<Context> ParentContext, llvm::StringRef Name, + const FunctionSelector &Selector); llvm::Error readGlobalFunctionBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch); @@ -944,7 +983,7 @@ APINotesReader::Implementation::getIdentifierString(IdentifierID ID) { } template <typename TableT> -void APINotesReader::Implementation::collectExactFunctionParameterSelectors( +void APINotesReader::Implementation::collectFunctionSelectorsForDiagnostics( TableT &Table, llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors) { static_assert(std::is_same_v<TableT, SerializedGlobalFunctionTable> || @@ -952,7 +991,7 @@ void APINotesReader::Implementation::collectExactFunctionParameterSelectors( constexpr bool IsCXXMethod = std::is_same_v<TableT, SerializedCXXMethodTable>; for (const FunctionTableKey &Key : Table.keys()) { - if (!Key.parameterTypeIDs) + if (!Key.parameterTypeIDs && !Key.objectSelector) continue; Selectors.push_back(APINotesFunctionSelectorKey{Key, IsCXXMethod}); @@ -976,6 +1015,26 @@ std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey( [this](llvm::StringRef S) { return getIdentifier(S); }); } +std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey( + uint32_t ParentContextID, llvm::StringRef Name, + const FunctionSelector &Selector) { + auto GetIdentifier = [this](llvm::StringRef S) { return getIdentifier(S); }; + if (Selector.Parameters) { + if (Selector.Object) + return getFunctionKeyImpl( + ParentContextID, Name, + llvm::ArrayRef<std::string>(*Selector.Parameters), *Selector.Object, + GetIdentifier); + return getFunctionKeyImpl(ParentContextID, Name, + llvm::ArrayRef<std::string>(*Selector.Parameters), + GetIdentifier); + } + if (Selector.Object) + return getFunctionKeyImpl(ParentContextID, Name, *Selector.Object, + GetIdentifier); + return getFunctionKeyImpl(ParentContextID, Name, GetIdentifier); +} + std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey( std::optional<Context> ParentContext, llvm::StringRef Name) { uint32_t ParentContextID = @@ -992,6 +1051,14 @@ std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey( return getFunctionKey(ParentContextID, Name, Parameters); } +std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey( + std::optional<Context> ParentContext, llvm::StringRef Name, + const FunctionSelector &Selector) { + uint32_t ParentContextID = + ParentContext ? ParentContext->id.Value : static_cast<uint32_t>(-1); + return getFunctionKey(ParentContextID, Name, Selector); +} + std::optional<SelectorID> APINotesReader::Implementation::getSelector(ObjCSelectorRef Selector) { if (!ObjCSelectorTable || !IdentifierTable) @@ -2414,7 +2481,15 @@ auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name) auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name, llvm::ArrayRef<std::string> Parameters) -> VersionedInfo<CXXMethodInfo> { - return lookupCXXMethodImpl(CtxID, Name, Parameters); + FunctionSelector Selector; + Selector.Parameters.emplace(Parameters.begin(), Parameters.end()); + return lookupCXXMethodImpl(CtxID, Name, Selector); +} + +auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name, + const FunctionSelector &Selector) + -> VersionedInfo<CXXMethodInfo> { + return lookupCXXMethodImpl(CtxID, Name, Selector); } std::optional<APINotesFunctionSelectorKey> @@ -2437,6 +2512,16 @@ APINotesReader::getCXXMethodSelectorKey( return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/true}; } +std::optional<APINotesFunctionSelectorKey> +APINotesReader::getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name, + const FunctionSelector &Selector) { + std::optional<FunctionTableKey> Key = + Implementation->getFunctionKey(CtxID.Value, Name, Selector); + if (!Key) + return std::nullopt; + return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/true}; +} + auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name) -> VersionedInfo<CXXMethodInfo> { if (!Implementation->CXXMethodTable) @@ -2454,15 +2539,14 @@ auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name) return {Implementation->SwiftVersion, *Known}; } -template <typename ParameterT> auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name, - llvm::ArrayRef<ParameterT> Parameters) + const FunctionSelector &Selector) -> VersionedInfo<CXXMethodInfo> { if (!Implementation->CXXMethodTable) return std::nullopt; std::optional<FunctionTableKey> Key = - Implementation->getFunctionKey(CtxID.Value, Name, Parameters); + Implementation->getFunctionKey(CtxID.Value, Name, Selector); if (!Key) return std::nullopt; @@ -2525,13 +2609,13 @@ APINotesReader::getGlobalFunctionSelectorKey( return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/false}; } -void APINotesReader::collectExactFunctionParameterSelectors( +void APINotesReader::collectFunctionSelectorsForDiagnostics( llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors) { if (Implementation->GlobalFunctionTable) - Implementation->collectExactFunctionParameterSelectors( + Implementation->collectFunctionSelectorsForDiagnostics( *Implementation->GlobalFunctionTable, Selectors); if (Implementation->CXXMethodTable) - Implementation->collectExactFunctionParameterSelectors( + Implementation->collectFunctionSelectorsForDiagnostics( *Implementation->CXXMethodTable, Selectors); } diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 86fee93ec4765..4027d096bf02a 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -158,6 +158,32 @@ class APINotesWriter::Implementation { return *Key; } + FunctionTableKey getFunctionKey(uint32_t ParentContextID, StringRef Name, + const FunctionSelector &Selector) { + std::optional<FunctionTableKey> Key; + auto GetIdentifier = [this](StringRef S) -> std::optional<IdentifierID> { + return getIdentifier(S); + }; + if (Selector.Parameters) { + if (Selector.Object) + Key = getFunctionKeyImpl( + ParentContextID, Name, + llvm::ArrayRef<std::string>(*Selector.Parameters), *Selector.Object, + GetIdentifier); + else + Key = getFunctionKeyImpl( + ParentContextID, Name, + llvm::ArrayRef<std::string>(*Selector.Parameters), GetIdentifier); + } else if (Selector.Object) { + Key = getFunctionKeyImpl(ParentContextID, Name, *Selector.Object, + GetIdentifier); + } else { + Key = getFunctionKeyImpl(ParentContextID, Name, GetIdentifier); + } + assert(Key && "Writer identifier lookup should not fail"); + return *Key; + } + FunctionTableKey getFunctionKey(std::optional<Context> ParentContext, StringRef Name) { uint32_t ParentContextID = @@ -173,6 +199,14 @@ class APINotesWriter::Implementation { return getFunctionKey(ParentContextID, Name, Parameters); } + FunctionTableKey getFunctionKey(std::optional<Context> ParentContext, + StringRef Name, + const FunctionSelector &Selector) { + uint32_t ParentContextID = + ParentContext ? ParentContext->id.Value : static_cast<uint32_t>(-1); + return getFunctionKey(ParentContextID, Name, Selector); + } + /// Retrieve the ID for the given selector. SelectorID getSelector(ObjCSelectorRef SelectorRef) { // Translate the selector reference into a stored selector. @@ -507,12 +541,42 @@ static unsigned getFunctionTableKeyLength(const FunctionTableKey &Key) { : 0); } +static uint8_t getFunctionTableKeyFlags(const FunctionTableKey &Key) { + uint8_t Flags = Key.parameterTypeIDs ? FunctionKeyHasParameterSelector : 0; + if (!Key.objectSelector) + return Flags; + + if (Key.objectSelector->Const) { + Flags |= FunctionKeyObjectConstPresent; + if (*Key.objectSelector->Const) + Flags |= FunctionKeyObjectConstValue; + } + if (Key.objectSelector->Volatile) { + Flags |= FunctionKeyObjectVolatilePresent; + if (*Key.objectSelector->Volatile) + Flags |= FunctionKeyObjectVolatileValue; + } + if (Key.objectSelector->Ref) { + Flags |= FunctionKeyObjectRefPresent; + switch (*Key.objectSelector->Ref) { + case FunctionObjectRefQualifier::None: + break; + case FunctionObjectRefQualifier::LValue: + Flags |= FunctionKeyObjectRefLValue; + break; + case FunctionObjectRefQualifier::RValue: + Flags |= FunctionKeyObjectRefRValue; + break; + } + } + return Flags; +} + static void emitFunctionTableKey(raw_ostream &OS, const FunctionTableKey &Key) { llvm::support::endian::Writer writer(OS, llvm::endianness::little); writer.write<uint32_t>(Key.parentContextID); writer.write<uint32_t>(Key.nameID); - writer.write<uint8_t>(Key.parameterTypeIDs ? FunctionKeyHasParameterSelector - : 0); + writer.write<uint8_t>(getFunctionTableKeyFlags(Key)); writer.write<uint16_t>(Key.parameterTypeIDs ? Key.parameterTypeIDs->size() : 0); if (Key.parameterTypeIDs) @@ -1631,6 +1695,15 @@ void APINotesWriter::addCXXMethod(ContextID CtxID, llvm::StringRef Name, Implementation->CXXMethods[Key].push_back({SwiftVersion, Info}); } +void APINotesWriter::addCXXMethod(ContextID CtxID, llvm::StringRef Name, + const FunctionSelector &Selector, + const CXXMethodInfo &Info, + VersionTuple SwiftVersion) { + FunctionTableKey Key = + Implementation->getFunctionKey(CtxID.Value, Name, Selector); + Implementation->CXXMethods[Key].push_back({SwiftVersion, Info}); +} + void APINotesWriter::addField(ContextID CtxID, llvm::StringRef Name, const FieldInfo &Info, VersionTuple SwiftVersion) { diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 4079675228a21..608152b2f4442 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -18,7 +18,6 @@ #include "clang/APINotes/Types.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/Specifiers.h" -#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringSet.h" @@ -347,6 +346,7 @@ typedef std::vector<StringRef> WhereParamsSeq; struct FunctionWhere { std::optional<WhereParamsSeq> Parameters; + std::optional<api_notes::FunctionObjectSelector> Object; }; struct Function { @@ -373,9 +373,27 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(Function) namespace llvm { namespace yaml { +template <> +struct ScalarEnumerationTraits<api_notes::FunctionObjectRefQualifier> { + static void enumeration(IO &IO, api_notes::FunctionObjectRefQualifier &Ref) { + IO.enumCase(Ref, "none", api_notes::FunctionObjectRefQualifier::None); + IO.enumCase(Ref, "lvalue", api_notes::FunctionObjectRefQualifier::LValue); + IO.enumCase(Ref, "rvalue", api_notes::FunctionObjectRefQualifier::RValue); + } +}; + +template <> struct MappingTraits<api_notes::FunctionObjectSelector> { + static void mapping(IO &IO, api_notes::FunctionObjectSelector &O) { + IO.mapOptional("Const", O.Const); + IO.mapOptional("Volatile", O.Volatile); + IO.mapOptional("Ref", O.Ref); + } +}; + template <> struct MappingTraits<FunctionWhere> { static void mapping(IO &IO, FunctionWhere &W) { IO.mapOptional("Parameters", W.Parameters); + IO.mapOptional("Object", W.Object); } }; @@ -793,19 +811,20 @@ bool clang::api_notes::parseAndDumpAPINotes(StringRef YI, namespace { using namespace api_notes; +static void appendDuplicateKeyPart(llvm::raw_ostream &OS, + llvm::StringRef Part) { + OS << Part.size() << ':' << Part; +} + static std::string -getFunctionSelectorKey(llvm::StringRef Name, - llvm::ArrayRef<llvm::StringRef> Parameters) { +getFunctionSelectorDuplicateKey(llvm::StringRef Name, + const FunctionSelector &Selector) { llvm::SmallString<64> Key; llvm::raw_svector_ostream OS(Key); - auto AppendKeyPart = [&OS](llvm::StringRef Part) { - OS << Part.size() << ':' << Part; - }; - - AppendKeyPart(Name); - OS << ';'; - for (llvm::StringRef Parameter : Parameters) - AppendKeyPart(Parameter); + appendDuplicateKeyPart(OS, Name); + std::string SelectorText = + api_notes::formatAPINotesFunctionSelector(Selector); + appendDuplicateKeyPart(OS, SelectorText); return Key.str().str(); } @@ -1064,16 +1083,38 @@ class YAMLConverter { TheNamespace.Items, SwiftVersion); } - std::pair<bool, std::optional<llvm::ArrayRef<llvm::StringRef>>> - getWhereParameters(const Function &Function) { + std::optional<FunctionSelector> getWhereSelector(const Function &Function, + bool AllowObject) { if (!Function.Where) - return {true, std::nullopt}; + return FunctionSelector{}; + + if (!Function.Where->Parameters && !Function.Where->Object) { + emitError("'Where' requires 'Parameters' or 'Object'"); + return std::nullopt; + } + + FunctionSelector Selector; + if (Function.Where->Parameters) { + Selector.Parameters.emplace(); + Selector.Parameters->reserve(Function.Where->Parameters->size()); + for (llvm::StringRef Parameter : *Function.Where->Parameters) + Selector.Parameters->push_back(Parameter.str()); + } - if (!Function.Where->Parameters) { - emitError("'Where' requires 'Parameters'"); - return {false, std::nullopt}; + if (Function.Where->Object) { + if (!AllowObject) { + emitError("'Object' is only supported on C++ methods"); + return std::nullopt; + } + if (!Function.Where->Object->Const && !Function.Where->Object->Volatile && + !Function.Where->Object->Ref) { + emitError("'Object' requires at least one field"); + return std::nullopt; + } + Selector.Object = Function.Where->Object; } - return {true, llvm::ArrayRef<llvm::StringRef>(*Function.Where->Parameters)}; + + return Selector; } template <typename FuncOrMethodInfo> @@ -1185,27 +1226,26 @@ class YAMLConverter { llvm::StringSet<> KnownMethodSelectors; for (const auto &CXXMethod : T.Methods) { - auto WhereParameters = getWhereParameters(CXXMethod); - if (!WhereParameters.first) + auto WhereSelector = getWhereSelector(CXXMethod, /*AllowObject=*/true); + if (!WhereSelector) continue; - if (WhereParameters.second) { - if (!KnownMethodSelectors - .insert(getFunctionSelectorKey(CXXMethod.Name, - *WhereParameters.second)) - .second) { + if (WhereSelector->Parameters || WhereSelector->Object) { + std::string DuplicateKey = + getFunctionSelectorDuplicateKey(CXXMethod.Name, *WhereSelector); + if (!KnownMethodSelectors.insert(DuplicateKey).second) { emitError(llvm::Twine("multiple API notes entries for C++ method '") + - CXXMethod.Name + "' with Where.Parameters " + - formatAPINotesParameterSelector(*WhereParameters.second)); + CXXMethod.Name + "' with " + + api_notes::formatAPINotesFunctionSelector(*WhereSelector)); continue; } } CXXMethodInfo MI; convertFunction(CXXMethod, MI); - if (WhereParameters.second) - Writer.addCXXMethod(TagCtxID, CXXMethod.Name, *WhereParameters.second, - MI, SwiftVersion); + if (WhereSelector->Parameters || WhereSelector->Object) + Writer.addCXXMethod(TagCtxID, CXXMethod.Name, *WhereSelector, MI, + SwiftVersion); else Writer.addCXXMethod(TagCtxID, CXXMethod.Name, MI, SwiftVersion); } @@ -1279,25 +1319,24 @@ class YAMLConverter { llvm::StringSet<> KnownNameOnlyFunctions; llvm::StringSet<> KnownFunctionSelectors; for (const auto &Function : TLItems.Functions) { - auto WhereParameters = getWhereParameters(Function); - if (!WhereParameters.first) + auto WhereSelector = getWhereSelector(Function, /*AllowObject=*/false); + if (!WhereSelector) continue; - if (WhereParameters.second) { - if (!KnownFunctionSelectors - .insert(getFunctionSelectorKey(Function.Name, - *WhereParameters.second)) - .second) { + if (WhereSelector->Parameters) { + std::string DuplicateKey = + getFunctionSelectorDuplicateKey(Function.Name, *WhereSelector); + if (!KnownFunctionSelectors.insert(DuplicateKey).second) { emitError( llvm::Twine("multiple API notes entries for global function '") + - Function.Name + "' with Where.Parameters " + - formatAPINotesParameterSelector(*WhereParameters.second)); + Function.Name + "' with " + + api_notes::formatAPINotesFunctionSelector(*WhereSelector)); continue; } } // Check for duplicate name-only global functions. - if (!WhereParameters.second && + if (!WhereSelector->Parameters && !KnownNameOnlyFunctions.insert(Function.Name).second) { emitError(llvm::Twine("multiple definitions of global function '") + Function.Name + "'"); @@ -1306,11 +1345,16 @@ class YAMLConverter { GlobalFunctionInfo GFI; convertFunction(Function, GFI); - if (WhereParameters.second) - Writer.addGlobalFunction(Ctx, Function.Name, *WhereParameters.second, - GFI, SwiftVersion); - else + if (WhereSelector->Parameters) { + llvm::SmallVector<llvm::StringRef, 4> ParameterRefs; + ParameterRefs.reserve(WhereSelector->Parameters->size()); + for (const std::string &Parameter : *WhereSelector->Parameters) + ParameterRefs.push_back(Parameter); + Writer.addGlobalFunction(Ctx, Function.Name, ParameterRefs, GFI, + SwiftVersion); + } else { Writer.addGlobalFunction(Ctx, Function.Name, GFI, SwiftVersion); + } } // Write all enumerators. diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 78153d9ddf39d..e7ddacf9d2515 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -23,6 +23,7 @@ #include "clang/Lex/Lexer.h" #include "clang/Sema/SemaObjC.h" #include "clang/Sema/SemaSwift.h" +#include "llvm/ADT/bit.h" #include <stack> using namespace clang; @@ -1085,7 +1086,7 @@ APINotesSelectorDiagnosticState::getOrCreateReaderState( return State; SmallVector<api_notes::APINotesFunctionSelectorKey, 4> Selectors; - Reader.collectExactFunctionParameterSelectors(Selectors); + Reader.collectFunctionSelectorsForDiagnostics(Selectors); State.addSelectors(Selectors); return State; } @@ -1112,6 +1113,58 @@ void APINotesSelectorDiagnosticReaderState::markCandidatesUsed( } } +static api_notes::FunctionObjectSelector +getAPINotesObjectSelector(const CXXMethodDecl *Method) { + api_notes::FunctionObjectSelector Selector; + Selector.Const = Method->isConst(); + Selector.Volatile = Method->isVolatile(); + switch (Method->getRefQualifier()) { + case RQ_None: + Selector.Ref = api_notes::FunctionObjectRefQualifier::None; + break; + case RQ_LValue: + Selector.Ref = api_notes::FunctionObjectRefQualifier::LValue; + break; + case RQ_RValue: + Selector.Ref = api_notes::FunctionObjectRefQualifier::RValue; + break; + } + return Selector; +} + +static void getAPINotesObjectSelectorSubsets( + api_notes::FunctionObjectSelector ObjectSelector, + SmallVectorImpl<api_notes::FunctionObjectSelector> &Subsets) { + enum ObjectSelectorField : unsigned { + ConstField = 1u << 0, + VolatileField = 1u << 1, + RefField = 1u << 2, + }; + + constexpr unsigned ObjectSelectorSubsetMasks[] = { + ConstField, + VolatileField, + RefField, + ConstField | VolatileField, + ConstField | RefField, + VolatileField | RefField, + ConstField | VolatileField | RefField, + }; + + // Apply less-constrained object selectors before more-constrained ones so + // entries that specify more Object fields can refine earlier effects. + for (unsigned Mask : ObjectSelectorSubsetMasks) { + api_notes::FunctionObjectSelector Subset; + if (Mask & ConstField) + Subset.Const = ObjectSelector.Const; + if (Mask & VolatileField) + Subset.Volatile = ObjectSelector.Volatile; + if (Mask & RefField) + Subset.Ref = ObjectSelector.Ref; + Subsets.push_back(Subset); + } +} + // Apply the first exact selector entry found. This preserves source-spelling // precedence over the desugared fallback and avoids applying multiple exact // entries for the same declaration. @@ -1397,21 +1450,20 @@ void Sema::ProcessAPINotes(Decl *D) { auto Info = Reader->lookupCXXMethod(Context->id, MethodName); ProcessVersionedAPINotes(*this, CXXMethod, Info); - if (ParameterSelectorCandidates) + auto &DiagnosticState = + getAPINotesSelectorDiagnosticState(*this, Reader); + if (auto NameOnlyKey = + Reader->getCXXMethodSelectorKey(Context->id, MethodName)) + DiagnosticState.noteSeenDeclaration(*NameOnlyKey, MethodName, + CXXMethod->getLocation()); + + if (ParameterSelectorCandidates) { processExactAPINotes<api_notes::CXXMethodInfo>( *this, CXXMethod, *ParameterSelectorCandidates, [&](ArrayRef<std::string> Parameters) { return Reader->lookupCXXMethod(Context->id, MethodName, Parameters); }); - - if (ParameterSelectorCandidates) { - auto &DiagnosticState = - getAPINotesSelectorDiagnosticState(*this, Reader); - if (auto BroadKey = - Reader->getCXXMethodSelectorKey(Context->id, MethodName)) - DiagnosticState.noteSeenDeclaration(*BroadKey, MethodName, - CXXMethod->getLocation()); DiagnosticState.markCandidatesUsed( [&](ArrayRef<std::string> Parameters) { return Reader->getCXXMethodSelectorKey( @@ -1419,6 +1471,52 @@ void Sema::ProcessAPINotes(Decl *D) { }, *ParameterSelectorCandidates); } + + if (!CXXMethod->isStatic()) { + SmallVector<api_notes::FunctionObjectSelector, 7> ObjectSelectors; + getAPINotesObjectSelectorSubsets( + getAPINotesObjectSelector(CXXMethod), ObjectSelectors); + // Apply every matching object selector in increasing specificity. + // Wildcard object constraints are broad refinements. Selectors + // that also constrain explicit parameters are applied last. + for (api_notes::FunctionObjectSelector ObjectSelector : + ObjectSelectors) { + api_notes::FunctionSelector Selector; + Selector.Object = ObjectSelector; + auto ObjectInfo = + Reader->lookupCXXMethod(Context->id, MethodName, Selector); + ProcessVersionedAPINotes(*this, CXXMethod, ObjectInfo); + if (auto ObjectKey = Reader->getCXXMethodSelectorKey( + Context->id, MethodName, Selector)) + DiagnosticState.markUsed(*ObjectKey); + } + + if (ParameterSelectorCandidates) { + for (api_notes::FunctionObjectSelector ObjectSelector : + ObjectSelectors) { + processExactAPINotes<api_notes::CXXMethodInfo>( + *this, CXXMethod, *ParameterSelectorCandidates, + [&](ArrayRef<std::string> Parameters) { + api_notes::FunctionSelector Selector; + Selector.Parameters.emplace(Parameters.begin(), + Parameters.end()); + Selector.Object = ObjectSelector; + return Reader->lookupCXXMethod(Context->id, MethodName, + Selector); + }); + DiagnosticState.markCandidatesUsed( + [&](ArrayRef<std::string> Parameters) { + api_notes::FunctionSelector Selector; + Selector.Parameters.emplace(Parameters.begin(), + Parameters.end()); + Selector.Object = ObjectSelector; + return Reader->getCXXMethodSelectorKey( + Context->id, MethodName, Selector); + }, + *ParameterSelectorCandidates); + } + } + } } } } @@ -1452,20 +1550,27 @@ void APINotesSelectorDiagnosticReaderState::diagnoseUnused( if (Selector.second) continue; - auto SeenName = - SeenNames.find(Selector.first.getWithoutParameterSelector()); + auto SeenName = SeenNames.find(Selector.first.getNameOnlyKey()); if (SeenName == SeenNames.end()) continue; - std::optional<SmallVector<std::string, 4>> ParameterSpellings = - Reader.getParameterSelectorSpellingsForDiagnostics(Selector.first); - if (!ParameterSpellings) - continue; + std::optional<SmallVector<std::string, 4>> ParameterSpellings; + if (Selector.first.Key.parameterTypeIDs) { + ParameterSpellings = + Reader.getParameterSelectorSpellingsForDiagnostics(Selector.first); + if (!ParameterSpellings) + continue; + } + + std::optional<ArrayRef<std::string>> ParameterRefs; + if (ParameterSpellings) + ParameterRefs = ArrayRef<std::string>(*ParameterSpellings); S.Diag(SeenName->second.Loc, diag::warn_apinotes_message) << (llvm::Twine("API notes entry for '") + SeenName->second.Name + - "' has unmatched Where.Parameters " + - api_notes::formatAPINotesParameterSelector(*ParameterSpellings)) + "' has unmatched " + + api_notes::formatAPINotesFunctionSelector( + ParameterRefs, Selector.first.Key.objectSelector)) .str(); } } diff --git a/clang/lib/Sema/SemaAPINotesInternal.h b/clang/lib/Sema/SemaAPINotesInternal.h index 5766343956ce5..b09968e166243 100644 --- a/clang/lib/Sema/SemaAPINotesInternal.h +++ b/clang/lib/Sema/SemaAPINotesInternal.h @@ -39,11 +39,11 @@ struct APINotesSelectorDiagnosticName { /// end-of-TU diagnostics can warn about exact selectors for known names that /// were never matched. struct APINotesSelectorDiagnosticReaderState { - /// Exact Where.Parameters selector keys stored by API notes. The bool is - /// true once Sema sees a declaration matching the exact selector. + /// Function selector keys stored by API notes. The bool is true once + /// Sema sees a declaration matching the selector. llvm::DenseMap<api_notes::APINotesFunctionSelectorKey, bool> SelectorUsed; - /// Maps broad/name-only keys to a declaration location/name used for + /// Maps broad name-only keys to a declaration location/name used for /// diagnostics. llvm::DenseMap<api_notes::APINotesFunctionSelectorKey, APINotesSelectorDiagnosticName> @@ -63,7 +63,7 @@ struct APINotesSelectorDiagnosticReaderState { void noteSeenDeclaration(const api_notes::APINotesFunctionSelectorKey &Key, llvm::StringRef Name, SourceLocation Loc) { - SeenNames.insert({Key.getWithoutParameterSelector(), {Loc, Name.str()}}); + SeenNames.insert({Key.getNameOnlyKey(), {Loc, Name.str()}}); } void markUsed(const api_notes::APINotesFunctionSelectorKey &Key) { diff --git a/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.apinotes b/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.apinotes new file mode 100644 index 0000000000000..5397d46fbd883 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.apinotes @@ -0,0 +1,96 @@ +--- +Name: WhereObjectQualifiers +Tags: +- Name: ObjectBuilder + Methods: + - Name: buildRef + Where: + Object: + Ref: lvalue + SwiftName: buildFromLValue() + - Name: buildRef + Where: + Parameters: [] + Object: + Ref: rvalue + SwiftName: buildFromRValue() + - Name: buildConst + Where: + Parameters: [] + Object: + Const: false + Ref: lvalue + SwiftName: buildMutableLValue() + - Name: buildConst + Where: + Parameters: [] + Object: + Const: true + Ref: lvalue + SwiftName: buildConstLValue() + - Name: buildVolatile + Where: + Parameters: [] + Object: + Volatile: false + SwiftName: buildNonVolatile() + - Name: buildVolatile + Where: + Parameters: [] + Object: + Volatile: true + SwiftName: buildVolatile() + - Name: buildNone + Where: + Parameters: [] + Object: + Ref: none + SwiftName: buildUnqualified() + - Name: buildCombined + Where: + Parameters: [] + Object: + Const: true + Volatile: true + Ref: lvalue + SwiftName: buildConstVolatileLValue() + - Name: buildConstRValue + Where: + Parameters: [] + Object: + Const: true + Ref: rvalue + SwiftName: buildConstRValue() + - Name: buildVolatileRValue + Where: + Parameters: [] + Object: + Volatile: true + Ref: rvalue + SwiftName: buildVolatileRValue() + - Name: buildObjectOnly + Where: + Object: + Ref: lvalue + SwiftName: buildObjectOnlyLValue(_:) + - Name: buildStatic + Where: + Parameters: [] + Object: + Ref: none + SwiftName: shouldNotApplyToStatic() + - Name: buildStaticObjectOnly + Where: + Object: + Ref: none + SwiftName: shouldNotApplyToStaticObjectOnly() + - Name: consume + Where: + Parameters: + - 'ObjectBuffer &' + SwiftName: consumeBorrowed(_:) + - Name: consume + Where: + Parameters: + - 'ObjectBuffer &&' + SwiftName: consumeOwned(_:) diff --git a/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.h b/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.h new file mode 100644 index 0000000000000..ed3c5ed92e8e1 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/WhereObjectQualifiers.h @@ -0,0 +1,30 @@ +#ifndef WHERE_OBJECT_QUALIFIERS_H +#define WHERE_OBJECT_QUALIFIERS_H + +struct ObjectBuffer {}; + +struct ObjectBuilder { + void buildRef() &; + void buildRef() &&; + + void buildConst() &; + void buildConst() const &; + + void buildVolatile(); + void buildVolatile() volatile; + + void buildNone(); + void buildCombined() const volatile &; + void buildConstRValue() const &&; + void buildVolatileRValue() volatile &&; + void buildObjectOnly(int) &; + void buildObjectOnly(double) &; + void buildObjectOnly(int) &&; + static void buildStatic(); + static void buildStaticObjectOnly(); + + void consume(ObjectBuffer &); + void consume(ObjectBuffer &&); +}; + +#endif // WHERE_OBJECT_QUALIFIERS_H diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap b/clang/test/APINotes/Inputs/Headers/module.modulemap index 592d482ea7a57..602e44273ff60 100644 --- a/clang/test/APINotes/Inputs/Headers/module.modulemap +++ b/clang/test/APINotes/Inputs/Headers/module.modulemap @@ -75,3 +75,8 @@ module WhereParametersSema { header "WhereParametersSema.h" export * } + +module WhereObjectQualifiers { + header "WhereObjectQualifiers.h" + export * +} diff --git a/clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/APINotes.apinotes b/clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/APINotes.apinotes new file mode 100644 index 0000000000000..3a6a39c17d9c8 --- /dev/null +++ b/clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/APINotes.apinotes @@ -0,0 +1,46 @@ +--- +Name: WhereObjectQualifiers +Functions: +- Name: invalidGlobal + Where: + Parameters: [] + Object: + Ref: none + SwiftName: invalidGlobal() +Tags: +- Name: ObjectDiagBuilder + Methods: + - Name: objectOnly + Where: + Object: + Ref: none + SwiftName: objectOnly() + - Name: emptyObject + Where: + Parameters: [] + Object: {} + SwiftName: emptyObject() + - Name: duplicate + Where: + Parameters: [] + Object: + Ref: lvalue + SwiftName: duplicateOne() + - Name: duplicate + Where: + Parameters: [] + Object: + Ref: lvalue + SwiftName: duplicateTwo() + - Name: acceptedDifferentRef + Where: + Parameters: [] + Object: + Ref: lvalue + SwiftName: acceptedLValue() + - Name: acceptedDifferentRef + Where: + Parameters: [] + Object: + Ref: rvalue + SwiftName: acceptedRValue() diff --git a/clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/WhereObjectQualifiers.h b/clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/WhereObjectQualifiers.h new file mode 100644 index 0000000000000..6748c36ceb23c --- /dev/null +++ b/clang/test/APINotes/Inputs/WhereObjectQualifiersDiag/WhereObjectQualifiers.h @@ -0,0 +1,14 @@ +#ifndef WHERE_OBJECT_QUALIFIERS_DIAG_H +#define WHERE_OBJECT_QUALIFIERS_DIAG_H + +void invalidGlobal(); + +struct ObjectDiagBuilder { + void objectOnly(); + void emptyObject(); + void duplicate(); + void acceptedDifferentRef() &; + void acceptedDifferentRef() &&; +}; + +#endif // WHERE_OBJECT_QUALIFIERS_DIAG_H diff --git a/clang/test/APINotes/where-object-qualifiers.cpp b/clang/test/APINotes/where-object-qualifiers.cpp new file mode 100644 index 0000000000000..78eff1d56e3cd --- /dev/null +++ b/clang/test/APINotes/where-object-qualifiers.cpp @@ -0,0 +1,63 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -fsyntax-only -I %S/Inputs/Headers %s -x c++ +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildRef -x c++ | FileCheck --check-prefix=REF %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildConst -x c++ | FileCheck --check-prefix=CONST %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildVolatile -x c++ | FileCheck --check-prefix=VOLATILE %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildNone -x c++ | FileCheck --check-prefix=NONE %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildCombined -x c++ | FileCheck --check-prefix=COMBINED-CVREF %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildConstRValue -x c++ | FileCheck --check-prefix=COMBINED-CONST-RVALUE %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildVolatileRValue -x c++ | FileCheck --check-prefix=COMBINED-VOLATILE-RVALUE %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildObjectOnly -x c++ | FileCheck --check-prefix=OBJECT-ONLY %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::buildStatic -x c++ | FileCheck --check-prefix=STATIC %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiers -fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter ObjectBuilder::consume -x c++ | FileCheck --check-prefix=PARAM-REF %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereObjectQualifiersWarnings -fdisable-module-hash -fapinotes-modules -Wapinotes -fsyntax-only -I %S/Inputs/Headers %s -x c++ 2>&1 | FileCheck --check-prefix=UNMATCHED %s +// RUN: not %clang_cc1 -fsyntax-only -fapinotes %s -I %S/Inputs/WhereObjectQualifiersDiag 2>&1 | FileCheck --check-prefix=DIAG %s + +#include "WhereObjectQualifiers.h" + +// REF: CXXMethodDecl {{.+}} buildRef 'void () &{{.*}}' +// REF: SwiftNameAttr {{.+}} "buildFromLValue()" +// REF: CXXMethodDecl {{.+}} buildRef 'void () &&{{.*}}' +// REF: SwiftNameAttr {{.+}} "buildFromRValue()" + +// CONST: CXXMethodDecl {{.+}} buildConst 'void () &{{.*}}' +// CONST: SwiftNameAttr {{.+}} "buildMutableLValue()" +// CONST: CXXMethodDecl {{.+}} buildConst 'void () const &{{.*}}' +// CONST: SwiftNameAttr {{.+}} "buildConstLValue()" + +// VOLATILE: CXXMethodDecl {{.+}} buildVolatile 'void (){{.*}}' +// VOLATILE: SwiftNameAttr {{.+}} "buildNonVolatile()" +// VOLATILE: CXXMethodDecl {{.+}} buildVolatile 'void () volatile{{.*}}' +// VOLATILE: SwiftNameAttr {{.+}} "buildVolatile()" + +// NONE: CXXMethodDecl {{.+}} buildNone 'void (){{.*}}' +// NONE: SwiftNameAttr {{.+}} "buildUnqualified()" + +// COMBINED-CVREF: CXXMethodDecl {{.+}} buildCombined 'void () const volatile &{{.*}}' +// COMBINED-CVREF: SwiftNameAttr {{.+}} "buildConstVolatileLValue()" +// COMBINED-CONST-RVALUE: CXXMethodDecl {{.+}} buildConstRValue 'void () const &&{{.*}}' +// COMBINED-CONST-RVALUE: SwiftNameAttr {{.+}} "buildConstRValue()" +// COMBINED-VOLATILE-RVALUE: CXXMethodDecl {{.+}} buildVolatileRValue 'void () volatile &&{{.*}}' +// COMBINED-VOLATILE-RVALUE: SwiftNameAttr {{.+}} "buildVolatileRValue()" + +// OBJECT-ONLY: CXXMethodDecl {{.+}} buildObjectOnly 'void (int) &{{.*}}' +// OBJECT-ONLY: SwiftNameAttr {{.+}} "buildObjectOnlyLValue(_:)" +// OBJECT-ONLY: CXXMethodDecl {{.+}} buildObjectOnly 'void (double) &{{.*}}' +// OBJECT-ONLY: SwiftNameAttr {{.+}} "buildObjectOnlyLValue(_:)" +// OBJECT-ONLY: CXXMethodDecl {{.+}} buildObjectOnly 'void (int) &&{{.*}}' +// OBJECT-ONLY-NOT: SwiftNameAttr + +// STATIC: CXXMethodDecl {{.+}} buildStatic 'void ()' static +// STATIC-NOT: SwiftNameAttr + +// UNMATCHED-DAG: warning: API notes entry for 'buildStatic' has unmatched Where.Parameters [] Object{Ref: none} +// UNMATCHED-DAG: warning: API notes entry for 'buildStaticObjectOnly' has unmatched Where.Object Object{Ref: none} + +// PARAM-REF: CXXMethodDecl {{.+}} consume 'void (ObjectBuffer &)' +// PARAM-REF: SwiftNameAttr {{.+}} "consumeBorrowed(_:)" +// PARAM-REF: CXXMethodDecl {{.+}} consume 'void (ObjectBuffer &&)' +// PARAM-REF: SwiftNameAttr {{.+}} "consumeOwned(_:)" + +// DIAG-DAG: error: 'Object' is only supported on C++ methods +// DIAG-DAG: error: 'Object' requires at least one field +// DIAG-DAG: error: multiple API notes entries for C++ method 'duplicate' with Where.Parameters [] Object{Ref: lvalue} >From 5bd5ba6cdb86f29d02a2b85badb8eb333b9708a1 Mon Sep 17 00:00:00 2001 From: stoeckoverflow <[email protected]> Date: Mon, 17 Aug 2026 15:07:53 +0200 Subject: [PATCH 2/2] [APINotes] Address Where.Object selector review feedback --- clang/include/clang/APINotes/Types.h | 48 +++++++++------------ clang/lib/APINotes/APINotesFormat.h | 48 ++++++++++----------- clang/lib/APINotes/APINotesReader.cpp | 6 +-- clang/lib/APINotes/APINotesWriter.cpp | 6 +-- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 20 ++++----- clang/lib/Sema/SemaAPINotes.cpp | 33 +++++--------- 6 files changed, 70 insertions(+), 91 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 4151e26a98b45..7ca3fff6f3718 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_APINOTES_TYPES_H #define LLVM_CLANG_APINOTES_TYPES_H +#include "clang/AST/TypeBase.h" #include "clang/Basic/Specifiers.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMapInfo.h" @@ -1008,19 +1009,13 @@ struct Context { using IdentifierID = llvm::PointerEmbeddedInt<unsigned, 31>; -/// Describes the C++ implicit-object ref-qualifier portion of a method -/// selector. -enum class FunctionObjectRefQualifier : uint8_t { - None, - LValue, - RValue, -}; - /// Describes optional constraints on a C++ method's implicit object parameter. struct FunctionObjectSelector { std::optional<bool> Const; std::optional<bool> Volatile; - std::optional<FunctionObjectRefQualifier> Ref; + std::optional<RefQualifierKind> Ref; + + std::string format() const; }; inline bool operator==(const FunctionObjectSelector &LHS, @@ -1034,27 +1029,25 @@ inline bool operator!=(const FunctionObjectSelector &LHS, return !(LHS == RHS); } -inline std::string formatAPINotesObjectSelector(FunctionObjectSelector Object) { +inline std::string FunctionObjectSelector::format() const { std::string Result; llvm::raw_string_ostream OS(Result); llvm::SmallVector<std::string, 3> Parts; - if (Object.Const) - Parts.push_back(std::string("Const: ") + - (*Object.Const ? "true" : "false")); - if (Object.Volatile) - Parts.push_back(std::string("Volatile: ") + - (*Object.Volatile ? "true" : "false")); - if (Object.Ref) { + if (Const) + Parts.push_back(std::string("Const: ") + (*Const ? "true" : "false")); + if (Volatile) + Parts.push_back(std::string("Volatile: ") + (*Volatile ? "true" : "false")); + if (Ref) { std::string Ref = "Ref: "; - switch (*Object.Ref) { - case FunctionObjectRefQualifier::None: + switch (*this->Ref) { + case RQ_None: Ref += "none"; break; - case FunctionObjectRefQualifier::LValue: + case RQ_LValue: Ref += "lvalue"; break; - case FunctionObjectRefQualifier::RValue: + case RQ_RValue: Ref += "rvalue"; break; } @@ -1072,6 +1065,8 @@ inline std::string formatAPINotesObjectSelector(FunctionObjectSelector Object) { struct FunctionSelector { std::optional<llvm::SmallVector<std::string, 4>> Parameters; std::optional<FunctionObjectSelector> Object; + + std::string format() const; }; inline bool operator==(const FunctionSelector &LHS, @@ -1098,18 +1093,17 @@ inline std::string formatAPINotesFunctionSelector( Result += " "; else Result = "Where.Object "; - Result += formatAPINotesObjectSelector(*Object); + Result += Object->format(); } return Result; } -inline std::string -formatAPINotesFunctionSelector(const FunctionSelector &Selector) { +inline std::string FunctionSelector::format() const { std::optional<llvm::ArrayRef<std::string>> Parameters; - if (Selector.Parameters) - Parameters = llvm::ArrayRef<std::string>(*Selector.Parameters); - return formatAPINotesFunctionSelector(Parameters, Selector.Object); + if (this->Parameters) + Parameters = llvm::ArrayRef<std::string>(*this->Parameters); + return formatAPINotesFunctionSelector(Parameters, Object); } struct FunctionTableSelectorKey { diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index f0ea371e3ccef..f77706d04befb 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -12,6 +12,7 @@ #include "clang/APINotes/Types.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/PointerEmbeddedInt.h" +#include "llvm/ADT/STLFunctionalExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Bitcode/BitcodeConvenience.h" @@ -29,10 +30,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 42; // 39 for BoundsSafety; - // 40 for UnsafeBufferUsageAttr - // 41 for FunctionTableKey parameters - // 42 for FunctionTableKey object selectors +const uint16_t VERSION_MINOR = 42; // 42 for FunctionTableKey object selectors const uint8_t kSwiftConforms = 1; const uint8_t kSwiftDoesNotConform = 2; @@ -379,10 +377,10 @@ constexpr uint8_t FunctionKeyObjectSelectorMask = constexpr unsigned FunctionTableKeyBaseLength = sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint16_t); -template <typename GetIdentifierFn> -std::optional<FunctionTableKey> -getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, - GetIdentifierFn GetIdentifier) { +inline std::optional<FunctionTableKey> getFunctionKeyImpl( + uint32_t ParentContextID, llvm::StringRef Name, + llvm::function_ref<std::optional<IdentifierID>(llvm::StringRef)> + GetIdentifier) { std::optional<IdentifierID> NameID = GetIdentifier(Name); if (!NameID) return std::nullopt; @@ -390,11 +388,11 @@ getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, return FunctionTableKey(ParentContextID, *NameID); } -template <typename GetIdentifierFn> -std::optional<FunctionTableKey> -getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, - FunctionObjectSelector ObjectSelector, - GetIdentifierFn GetIdentifier) { +inline std::optional<FunctionTableKey> getFunctionKeyImpl( + uint32_t ParentContextID, llvm::StringRef Name, + FunctionObjectSelector ObjectSelector, + llvm::function_ref<std::optional<IdentifierID>(llvm::StringRef)> + GetIdentifier) { std::optional<IdentifierID> NameID = GetIdentifier(Name); if (!NameID) return std::nullopt; @@ -404,11 +402,12 @@ getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, return FunctionTableKey(ParentContextID, *NameID, std::move(Selector)); } -template <typename ParameterT, typename GetIdentifierFn> -std::optional<FunctionTableKey> -getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, - llvm::ArrayRef<ParameterT> Parameters, - GetIdentifierFn GetIdentifier) { +template <typename ParameterT> +std::optional<FunctionTableKey> getFunctionKeyImpl( + uint32_t ParentContextID, llvm::StringRef Name, + llvm::ArrayRef<ParameterT> Parameters, + llvm::function_ref<std::optional<IdentifierID>(llvm::StringRef)> + GetIdentifier) { std::optional<IdentifierID> NameID = GetIdentifier(Name); if (!NameID) return std::nullopt; @@ -427,12 +426,13 @@ getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, return FunctionTableKey(ParentContextID, *NameID, std::move(Selector)); } -template <typename ParameterT, typename GetIdentifierFn> -std::optional<FunctionTableKey> -getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, - llvm::ArrayRef<ParameterT> Parameters, - FunctionObjectSelector ObjectSelector, - GetIdentifierFn GetIdentifier) { +template <typename ParameterT> +std::optional<FunctionTableKey> getFunctionKeyImpl( + uint32_t ParentContextID, llvm::StringRef Name, + llvm::ArrayRef<ParameterT> Parameters, + FunctionObjectSelector ObjectSelector, + llvm::function_ref<std::optional<IdentifierID>(llvm::StringRef)> + GetIdentifier) { std::optional<IdentifierID> NameID = GetIdentifier(Name); if (!NameID) return std::nullopt; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index ab06d75826d1a..b291cdded9a1b 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -95,11 +95,11 @@ static FunctionTableKey readFunctionTableKey(const uint8_t *Data, (FunctionKeyFlags & FunctionKeyObjectVolatileValue) != 0; if (FunctionKeyFlags & FunctionKeyObjectRefPresent) { if (FunctionKeyFlags & FunctionKeyObjectRefLValue) - Selector.Ref = FunctionObjectRefQualifier::LValue; + Selector.Ref = RQ_LValue; else if (FunctionKeyFlags & FunctionKeyObjectRefRValue) - Selector.Ref = FunctionObjectRefQualifier::RValue; + Selector.Ref = RQ_RValue; else - Selector.Ref = FunctionObjectRefQualifier::None; + Selector.Ref = RQ_None; } ObjectSelector = Selector; } diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 4027d096bf02a..b8231e28f4bef 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -559,12 +559,12 @@ static uint8_t getFunctionTableKeyFlags(const FunctionTableKey &Key) { if (Key.objectSelector->Ref) { Flags |= FunctionKeyObjectRefPresent; switch (*Key.objectSelector->Ref) { - case FunctionObjectRefQualifier::None: + case RQ_None: break; - case FunctionObjectRefQualifier::LValue: + case RQ_LValue: Flags |= FunctionKeyObjectRefLValue; break; - case FunctionObjectRefQualifier::RValue: + case RQ_RValue: Flags |= FunctionKeyObjectRefRValue; break; } diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 608152b2f4442..77c93b703367f 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -373,12 +373,11 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(Function) namespace llvm { namespace yaml { -template <> -struct ScalarEnumerationTraits<api_notes::FunctionObjectRefQualifier> { - static void enumeration(IO &IO, api_notes::FunctionObjectRefQualifier &Ref) { - IO.enumCase(Ref, "none", api_notes::FunctionObjectRefQualifier::None); - IO.enumCase(Ref, "lvalue", api_notes::FunctionObjectRefQualifier::LValue); - IO.enumCase(Ref, "rvalue", api_notes::FunctionObjectRefQualifier::RValue); +template <> struct ScalarEnumerationTraits<clang::RefQualifierKind> { + static void enumeration(IO &IO, clang::RefQualifierKind &Ref) { + IO.enumCase(Ref, "none", clang::RQ_None); + IO.enumCase(Ref, "lvalue", clang::RQ_LValue); + IO.enumCase(Ref, "rvalue", clang::RQ_RValue); } }; @@ -822,8 +821,7 @@ getFunctionSelectorDuplicateKey(llvm::StringRef Name, llvm::SmallString<64> Key; llvm::raw_svector_ostream OS(Key); appendDuplicateKeyPart(OS, Name); - std::string SelectorText = - api_notes::formatAPINotesFunctionSelector(Selector); + std::string SelectorText = Selector.format(); appendDuplicateKeyPart(OS, SelectorText); return Key.str().str(); } @@ -1235,8 +1233,7 @@ class YAMLConverter { getFunctionSelectorDuplicateKey(CXXMethod.Name, *WhereSelector); if (!KnownMethodSelectors.insert(DuplicateKey).second) { emitError(llvm::Twine("multiple API notes entries for C++ method '") + - CXXMethod.Name + "' with " + - api_notes::formatAPINotesFunctionSelector(*WhereSelector)); + CXXMethod.Name + "' with " + WhereSelector->format()); continue; } } @@ -1329,8 +1326,7 @@ class YAMLConverter { if (!KnownFunctionSelectors.insert(DuplicateKey).second) { emitError( llvm::Twine("multiple API notes entries for global function '") + - Function.Name + "' with " + - api_notes::formatAPINotesFunctionSelector(*WhereSelector)); + Function.Name + "' with " + WhereSelector->format()); continue; } } diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index e7ddacf9d2515..b701baca1bc83 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -1118,17 +1118,7 @@ getAPINotesObjectSelector(const CXXMethodDecl *Method) { api_notes::FunctionObjectSelector Selector; Selector.Const = Method->isConst(); Selector.Volatile = Method->isVolatile(); - switch (Method->getRefQualifier()) { - case RQ_None: - Selector.Ref = api_notes::FunctionObjectRefQualifier::None; - break; - case RQ_LValue: - Selector.Ref = api_notes::FunctionObjectRefQualifier::LValue; - break; - case RQ_RValue: - Selector.Ref = api_notes::FunctionObjectRefQualifier::RValue; - break; - } + Selector.Ref = Method->getRefQualifier(); return Selector; } @@ -1138,17 +1128,17 @@ static void getAPINotesObjectSelectorSubsets( enum ObjectSelectorField : unsigned { ConstField = 1u << 0, VolatileField = 1u << 1, - RefField = 1u << 2, + RefQualifierField = 1u << 2, }; constexpr unsigned ObjectSelectorSubsetMasks[] = { ConstField, VolatileField, - RefField, + RefQualifierField, ConstField | VolatileField, - ConstField | RefField, - VolatileField | RefField, - ConstField | VolatileField | RefField, + ConstField | RefQualifierField, + VolatileField | RefQualifierField, + ConstField | VolatileField | RefQualifierField, }; // Apply less-constrained object selectors before more-constrained ones so @@ -1159,7 +1149,7 @@ static void getAPINotesObjectSelectorSubsets( Subset.Const = ObjectSelector.Const; if (Mask & VolatileField) Subset.Volatile = ObjectSelector.Volatile; - if (Mask & RefField) + if (Mask & RefQualifierField) Subset.Ref = ObjectSelector.Ref; Subsets.push_back(Subset); } @@ -1562,15 +1552,14 @@ void APINotesSelectorDiagnosticReaderState::diagnoseUnused( continue; } - std::optional<ArrayRef<std::string>> ParameterRefs; + api_notes::FunctionSelector FunctionSelector; if (ParameterSpellings) - ParameterRefs = ArrayRef<std::string>(*ParameterSpellings); + FunctionSelector.Parameters = *ParameterSpellings; + FunctionSelector.Object = Selector.first.Key.objectSelector; S.Diag(SeenName->second.Loc, diag::warn_apinotes_message) << (llvm::Twine("API notes entry for '") + SeenName->second.Name + - "' has unmatched " + - api_notes::formatAPINotesFunctionSelector( - ParameterRefs, Selector.first.Key.objectSelector)) + "' has unmatched " + FunctionSelector.format()) .str(); } } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
