From 39a8c180bc83f58a95ddfb66439aceb718edf468 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 8 Aug 2024 18:07:49 -0500 Subject: [PATCH 1/5] TProtoClass: do not complaint about innards of transients. Do not complain about missing information about data members that directly or indirectly within a transient members of the top level class in GetRealData. This solves the underlying problem from https://github.com/root-project/root/pull/15733 --- core/meta/inc/TProtoClass.h | 4 ++-- core/meta/src/TClass.cxx | 2 +- core/meta/src/TProtoClass.cxx | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/meta/inc/TProtoClass.h b/core/meta/inc/TProtoClass.h index 45e2b437e7e3e..4aee2a489cad2 100644 --- a/core/meta/inc/TProtoClass.h +++ b/core/meta/inc/TProtoClass.h @@ -56,7 +56,7 @@ class TProtoClass: public TNamed { TProtoRealData() : fOffset(0), fDMIndex(-1), fLevel(0), fClassIndex(-1), fStatusFlag(0) {} TProtoRealData(const TRealData *rd); virtual ~TProtoRealData(); - TRealData *CreateRealData(TClass *currentClass, TClass *parent, TRealData * parentData, int prevLevel) const; + TRealData *CreateRealData(TClass *currentClass, TClass *parent, TRealData * parentData, int prevLevel, bool quiet) const; Bool_t TestFlag(UInt_t f) const { return (Bool_t) ((fStatusFlag & f) != 0); } void SetFlag(UInt_t f, Bool_t on = kTRUE) { @@ -91,7 +91,7 @@ class TProtoClass: public TNamed { // compute index of data member in the list static Int_t DataMemberIndex(TClass * cl, const char * name); // find data member given an index - static TDataMember * FindDataMember(TClass * cl, Int_t index); + static TDataMember * FindDataMember(TClass * cl, Int_t index, bool quiet); public: TProtoClass(): diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index 4ea5f8dcac0e8..f312b520ae0d7 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -775,7 +775,7 @@ void TBuildRealData::Inspect(TClass* cl, const char* pname, const char* mname, c return; } - Bool_t isTransientMember = kFALSE; + Bool_t isTransientMember = isTransient; if (!dm->IsPersistent()) { // For the DataModelEvolution we need access to the transient member. diff --git a/core/meta/src/TProtoClass.cxx b/core/meta/src/TProtoClass.cxx index 26674c1cedd79..d115e86b0b052 100644 --- a/core/meta/src/TProtoClass.cxx +++ b/core/meta/src/TProtoClass.cxx @@ -378,7 +378,7 @@ Bool_t TProtoClass::FillTClass(TClass* cl) { //TProtoRealData* prd = (TProtoRealData*)element; // pass a previous real data only if depth - if (TRealData* rd = element.CreateRealData(currentRDClass, cl,prevRealData, prevLevel)) { + if (TRealData* rd = element.CreateRealData(currentRDClass, cl,prevRealData, prevLevel, element.TestFlag(TProtoRealData::kIsTransient))) { if (first) { //LM: need to do here because somehow fRealData is destroyed when calling TClass::GetListOfDataMembers() if (cl->fRealData) { @@ -465,11 +465,11 @@ TProtoClass::TProtoRealData::~TProtoRealData() /// find data member from protoclass TRealData* TProtoClass::TProtoRealData::CreateRealData(TClass* dmClass, - TClass* parent, TRealData *prevData, int prevLevel) const + TClass* parent, TRealData *prevData, int prevLevel, bool quiet) const { //TDataMember* dm = (TDataMember*)dmClass->GetListOfDataMembers()->FindObject(fName); - TDataMember* dm = TProtoClass::FindDataMember(dmClass, fDMIndex); + TDataMember* dm = TProtoClass::FindDataMember(dmClass, fDMIndex, quiet); if (!dm && dmClass->GetState()!=TClass::kForwardDeclared && !dmClass->fIsSyntheticPair) { ::Error("CreateRealData", @@ -558,7 +558,7 @@ Int_t TProtoClass::DataMemberIndex(TClass * cl, const char * name) } //////////////////////////////////////////////////////////////////////////////// -TDataMember * TProtoClass::FindDataMember(TClass * cl, Int_t index) +TDataMember * TProtoClass::FindDataMember(TClass * cl, Int_t index, bool quiet) { TList * dmList = cl->GetListOfDataMembers(false); @@ -572,7 +572,7 @@ TDataMember * TProtoClass::FindDataMember(TClass * cl, Int_t index) return dm; i++; } - if (cl->GetState()!=TClass::kForwardDeclared && !cl->fIsSyntheticPair) + if (cl->GetState()!=TClass::kForwardDeclared && !cl->fIsSyntheticPair && !quiet) ::Error("TProtoClass::FindDataMember","data member with index %d is not found in class %s",index,cl->GetName()); return nullptr; } From 322955af2e3b37b5bcbaf349e3c581407440c3b1 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Fri, 9 Aug 2024 14:37:48 -0500 Subject: [PATCH 2/5] TClass Real Data: proper tracking of transientness. We do want to record as transient a real data member that is inside a nested object that is itself marked as transient. This is particular useful to know when to warn about missing information and when not. Previously only the data member explicitly marked as transient were marked as transient in the RealData. Now we also mark the member nested inside a transient member. This will prevent spurrious warning about missing information about the inner content of a transient data member. However we can not rely on when the `isTransient` parameter to `TBuildRealData::Inspect` is set to true because it is set both in the case where the object is really nested and the real data is being added to the current class (what we want) and in the case where we want to setup a related class but the real data will not be added to the current class (eg. collection or pointer to a class). --- core/base/inc/TMemberInspector.h | 5 +++++ core/base/src/TMemberInspector.cxx | 2 +- core/meta/src/TClass.cxx | 10 +++++++--- core/metacling/src/TCling.cxx | 5 ++++- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/core/base/inc/TMemberInspector.h b/core/base/inc/TMemberInspector.h index c47a8fdc32f33..b6e4979944a29 100644 --- a/core/base/inc/TMemberInspector.h +++ b/core/base/inc/TMemberInspector.h @@ -39,6 +39,7 @@ class TMemberInspector { class TParentBuf; TParentBuf* fParent; // current inspection "path" EObjectPointerState fObjectPointerState; // whether the address is valid or only an offset + UInt_t fNestedTransient; TMemberInspector(const TMemberInspector &) = delete; TMemberInspector &operator=(const TMemberInspector &) = delete; @@ -76,6 +77,10 @@ class TMemberInspector { void GenericShowMembers(const char *topClassName, const void *obj, Bool_t transientMember); + void DecrementNestedTransient() { --fNestedTransient; } + void IncrementNestedTransient() { ++fNestedTransient; } + bool IsNestedTransient() { return fNestedTransient != 0; } + ClassDef(TMemberInspector,0) //ABC for inspecting class data members }; diff --git a/core/base/src/TMemberInspector.cxx b/core/base/src/TMemberInspector.cxx index cbd70eb208f27..466298f904611 100644 --- a/core/base/src/TMemberInspector.cxx +++ b/core/base/src/TMemberInspector.cxx @@ -60,7 +60,7 @@ void TMemberInspector::TParentBuf::Remove(Ssiz_t startingAt) ClassImp(TMemberInspector); TMemberInspector::TMemberInspector(): - fObjectPointerState(kUnset) + fObjectPointerState(kUnset), fNestedTransient(0) { // Construct a member inspector diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index f312b520ae0d7..a0da33404b9a9 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -775,7 +775,7 @@ void TBuildRealData::Inspect(TClass* cl, const char* pname, const char* mname, c return; } - Bool_t isTransientMember = isTransient; + Bool_t isTransientMember = kFALSE; if (!dm->IsPersistent()) { // For the DataModelEvolution we need access to the transient member. @@ -824,6 +824,8 @@ void TBuildRealData::Inspect(TClass* cl, const char* pname, const char* mname, c TRealData::GetName(rdName,dm); rname += rdName; TRealData* rd = new TRealData(rname.Data(), offset, dm); + if (isTransientMember || IsNestedTransient()) + rd->SetBit(TRealData::kTransient); fRealDataClass->GetListOfRealData()->Add(rd); return; } @@ -833,12 +835,14 @@ void TBuildRealData::Inspect(TClass* cl, const char* pname, const char* mname, c if (dm->IsaPointer()) { // Data member is a pointer. TRealData* rd = new TRealData(rname, offset, dm); - if (isTransientMember) { rd->SetBit(TRealData::kTransient); }; + if (isTransientMember || IsNestedTransient()) + rd->SetBit(TRealData::kTransient); fRealDataClass->GetListOfRealData()->Add(rd); } else { // Data Member is a basic data type. TRealData* rd = new TRealData(rname, offset, dm); - if (isTransientMember) { rd->SetBit(TRealData::kTransient); }; + if (isTransientMember || IsNestedTransient()) + rd->SetBit(TRealData::kTransient); if (!dm->IsBasic()) { rd->SetIsObject(kTRUE); diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index 3fcc4e698d0c8..f769a22905a66 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -2980,9 +2980,12 @@ void TCling::InspectMembers(TMemberInspector& insp, const void* obj, // if we can not find the member (which should not really happen), // let's consider it transient. Bool_t transient = isTransient || !mbr || !mbr->IsPersistent(); - + if (!mbr || !mbr->IsPersistent()) + insp.IncrementNestedTransient(); insp.InspectMember(sFieldRecName.c_str(), cobj + fieldOffset, (fieldName + '.').c_str(), transient); + if (!mbr || !mbr->IsPersistent()) + insp.DecrementNestedTransient(); } } From 0291a266fa262e0e08b05b632b6c848cf62d3b6d Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Fri, 9 Aug 2024 17:23:48 -0500 Subject: [PATCH 3/5] std::tuple: properly reload ClassInfo and StreamerInfo. When/if a TClass for a std::tuple is first loaded in forward declared state and then the interpreter information is loaded, we now properly replace the ClassInfo and StreamerInfo --- core/metacling/src/TCling.cxx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index f769a22905a66..db93236d66740 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -6663,6 +6663,18 @@ void TCling::RefreshClassInfo(TClass *cl, const clang::NamedDecl *def, bool alia } } else if (!cl->TestBit(TClass::kLoading) && !cl->fHasRootPcmInfo) { cl->ResetCaches(); + if (strncmp(cl->GetName(),"tuple<",strlen("tuple<"))==0) { + // We need to use the Emulated Tuple but we should not trigger parsing + // yet, so delay the creation of the ClassInfo + delete ((TClingClassInfo *)cl->fClassInfo); + cl->fClassInfo = nullptr; + cl->fCanLoadClassInfo = true; + cl->RemoveStreamerInfo(cl->fClassVersion); + if (cl->fState != TClass::kHasTClassInit) { + cl->fState = TClass::kInterpreted; + } + return; + } // yes, this is almost a waste of time, but we do need to lookup // the 'type' corresponding to the TClass anyway in order to // preserve the opaque typedefs (Double32_t) From 1497c3ffe823b6036ddfdd7b5e1a740ca9eb546a Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Wed, 21 Aug 2024 17:22:29 -0500 Subject: [PATCH 4/5] TProtoClass: add doc string for CreateRealData --- core/meta/src/TProtoClass.cxx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/meta/src/TProtoClass.cxx b/core/meta/src/TProtoClass.cxx index d115e86b0b052..a42a42b5b0b82 100644 --- a/core/meta/src/TProtoClass.cxx +++ b/core/meta/src/TProtoClass.cxx @@ -461,8 +461,16 @@ TProtoClass::TProtoRealData::~TProtoRealData() } //////////////////////////////////////////////////////////////////////////////// -/// Create a TRealData from this, with its data member coming from dmClass. +/// \brief Create a TRealData from this, with its data member coming from dmClass. /// find data member from protoclass +/// +/// \return the created TRealData +/// +/// \param [in] dmClass Class where the data member is declared +/// \param [in] parent Parent class +/// \param [in] prevData the previous 'real' data member (might be part of another class) +/// \param [in] prevLevel nesting level +/// \param [in] quiet Whether we should not warn about missing information (usually set to true for transient members) TRealData* TProtoClass::TProtoRealData::CreateRealData(TClass* dmClass, TClass* parent, TRealData *prevData, int prevLevel, bool quiet) const From bea2dd7e8d695ddbd6e365dd8b975a1b97884d8b Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Wed, 21 Aug 2024 17:25:37 -0500 Subject: [PATCH 5/5] TProtoClass: add doc string for FindDataMember --- core/meta/src/TProtoClass.cxx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/meta/src/TProtoClass.cxx b/core/meta/src/TProtoClass.cxx index a42a42b5b0b82..1f96ee2957e70 100644 --- a/core/meta/src/TProtoClass.cxx +++ b/core/meta/src/TProtoClass.cxx @@ -565,6 +565,14 @@ Int_t TProtoClass::DataMemberIndex(TClass * cl, const char * name) return -1; } //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// \brief Find the requested TDataMember +/// +/// \return the requested TDataMember if found +/// +/// \param [in] cl TClass to search for the data member +/// \param [in] index Numerical index of the object's data member +/// \param [in] quiet Whether we should not warn about missing information (usually set to true for transient members) TDataMember * TProtoClass::FindDataMember(TClass * cl, Int_t index, bool quiet) {