diff --git a/core/cont/src/TClassTable.cxx b/core/cont/src/TClassTable.cxx index 9869372018d9b..d724ac86e83a4 100644 --- a/core/cont/src/TClassTable.cxx +++ b/core/cont/src/TClassTable.cxx @@ -407,6 +407,9 @@ void TClassTable::Add(TProtoClass *proto) if (r->fName) { if (r->fProto) delete r->fProto; r->fProto = proto; + TClass *oldcl = (TClass*)gROOT->GetListOfClasses()->FindObject(cname); + if (oldcl && oldcl->GetState() == TClass::kHasTClassInit) + proto->FillTClass(oldcl); return; } else if (ROOT::Internal::gROOTLocal && gCling) { TClass *oldcl = (TClass*)gROOT->GetListOfClasses()->FindObject(cname); diff --git a/core/dictgen/res/SelectionRules.h b/core/dictgen/res/SelectionRules.h index 8365064180855..01bb186be3edd 100644 --- a/core/dictgen/res/SelectionRules.h +++ b/core/dictgen/res/SelectionRules.h @@ -157,7 +157,7 @@ class SelectionRules { // These method are called from clr-scan and return true if the Decl selected, false otherwise //const BaseSelectionRule *IsDeclSelected(clang::Decl* D) const; - const ClassSelectionRule *IsDeclSelected(const clang::RecordDecl* D) const; + const ClassSelectionRule *IsDeclSelected(const clang::RecordDecl* D, bool includeTypedefRule) const; const ClassSelectionRule *IsDeclSelected(const clang::TypedefNameDecl* D) const; const ClassSelectionRule *IsDeclSelected(const clang::NamespaceDecl* D) const; const BaseSelectionRule *IsDeclSelected(const clang::EnumDecl* D) const; @@ -166,7 +166,7 @@ class SelectionRules { const BaseSelectionRule *IsDeclSelected(const clang::FunctionDecl* D) const; const BaseSelectionRule *IsDeclSelected(const clang::Decl* D) const; - const ClassSelectionRule *IsClassSelected(const clang::Decl* D, const std::string& qual_name) const; // is the class selected + const ClassSelectionRule *IsClassSelected(const clang::Decl* D, const std::string& qual_name, bool includeTypedefRule) const; // is the class selected const ClassSelectionRule *IsNamespaceSelected(const clang::Decl* D, const std::string& qual_name) const; // is the class selected // is the global function, variable, enum selected - the behavior is different for linkdef.h and selection.xml - that's why diff --git a/core/dictgen/src/Scanner.cxx b/core/dictgen/src/Scanner.cxx index c5507a5d7a78d..12dc128df3764 100644 --- a/core/dictgen/src/Scanner.cxx +++ b/core/dictgen/src/Scanner.cxx @@ -670,7 +670,7 @@ bool RScanner::TreatRecordDeclOrTypedefNameDecl(clang::TypeDecl* typeDecl) const ClassSelectionRule *selectedFromTypedef = typedefNameDecl ? fSelectionRules.IsDeclSelected(typedefNameDecl) : 0; - const ClassSelectionRule *selectedFromRecDecl = fSelectionRules.IsDeclSelected(recordDecl); + const ClassSelectionRule *selectedFromRecDecl = fSelectionRules.IsDeclSelected(recordDecl, false /* exclude typedef rules*/); const ClassSelectionRule *selected = typedefNameDecl ? selectedFromTypedef : selectedFromRecDecl; @@ -685,15 +685,17 @@ bool RScanner::TreatRecordDeclOrTypedefNameDecl(clang::TypeDecl* typeDecl) return true; // Save the typedef - if (selectedFromTypedef){ + if (selectedFromTypedef) { if (!IsElementPresent(fSelectedTypedefs, typedefNameDecl)) fSelectedTypedefs.push_back(typedefNameDecl); - // Early exit here if we are not in presence of XML - if (!fSelectionRules.IsSelectionXMLFile()) return true; + // Don't generate a dictionary for the class underlying a typedef found + // for a file name match (eg. "defined_in") + if (!selectedFromRecDecl && selectedFromTypedef->HasAttributeFileName()) + return true; } - if (fSelectionRules.IsSelectionXMLFile() && selected->IsFromTypedef()) { - if (!IsElementPresent(fSelectedTypedefs, typedefNameDecl)) + if (selected->IsFromTypedef()) { + if (typedefNameDecl && !IsElementPresent(fSelectedTypedefs, typedefNameDecl)) fSelectedTypedefs.push_back(typedefNameDecl); return true; } diff --git a/core/dictgen/src/SelectionRules.cxx b/core/dictgen/src/SelectionRules.cxx index acdd362ac0b70..fc574d027d49e 100644 --- a/core/dictgen/src/SelectionRules.cxx +++ b/core/dictgen/src/SelectionRules.cxx @@ -270,18 +270,18 @@ void SelectionRules::Optimize(){ fClassSelectionRules.remove_if(predicate); } -const ClassSelectionRule *SelectionRules::IsDeclSelected(const clang::RecordDecl *D) const +const ClassSelectionRule *SelectionRules::IsDeclSelected(const clang::RecordDecl *D, bool includeTypedefRule) const { std::string qual_name; GetDeclQualName(D,qual_name); - return IsClassSelected(D, qual_name); + return IsClassSelected(D, qual_name, includeTypedefRule); } const ClassSelectionRule *SelectionRules::IsDeclSelected(const clang::TypedefNameDecl *D) const { std::string qual_name; GetDeclQualName(D,qual_name); - return IsClassSelected(D, qual_name); + return IsClassSelected(D, qual_name, true); } const ClassSelectionRule *SelectionRules::IsDeclSelected(const clang::NamespaceDecl *D) const @@ -662,7 +662,7 @@ const ClassSelectionRule *SelectionRules::IsNamespaceSelected(const clang::Decl* } -const ClassSelectionRule *SelectionRules::IsClassSelected(const clang::Decl* D, const std::string& qual_name) const +const ClassSelectionRule *SelectionRules::IsClassSelected(const clang::Decl* D, const std::string& qual_name, bool includeTypedefRule) const { const clang::TagDecl* tagDecl = llvm::dyn_cast (D); //TagDecl has methods to understand of what kind is the Decl const clang::TypedefNameDecl* typeDefNameDecl = llvm::dyn_cast (D); @@ -699,6 +699,8 @@ const ClassSelectionRule *SelectionRules::IsClassSelected(const clang::Decl* D, const ClassSelectionRule* retval = nullptr; const clang::NamedDecl* nDecl(llvm::dyn_cast(D)); for(auto& rule : fClassSelectionRules) { + if (!includeTypedefRule && rule.IsFromTypedef()) + continue; BaseSelectionRule::EMatchType match = rule.Match(nDecl, qual_name, "", isLinkDefFile); if (match != BaseSelectionRule::kNoMatch) { // Check if the template must have its arguments manipulated diff --git a/core/meta/inc/TClass.h b/core/meta/inc/TClass.h index 0c9947f0743fa..6e2edee277ad4 100644 --- a/core/meta/inc/TClass.h +++ b/core/meta/inc/TClass.h @@ -223,7 +223,7 @@ friend class ROOT::Internal::TCheckHashRecursiveRemoveConsistency; Int_t fSizeof; //Sizeof the class. Int_t fCanSplit; //!Indicates whether this class can be split or not. - mutable std::atomic fProperty; //!Property + mutable std::atomic fProperty; //!Property See TClass::Property() for details mutable Long_t fClassProperty; //!C++ Property of the class (is abstract, has virtual table, etc.) // fHasRootPcmInfo needs to be atomic as long as GetListOfBases needs to modify it. diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index 23f553f141221..1dcef9b37e395 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -1473,9 +1473,8 @@ void TClass::Init(const char *name, Version_t cversion, fCanLoadClassInfo = kTRUE; // Here we check and grab the info from the rootpcm. TProtoClass *proto = TClassTable::GetProtoNorm(GetName()); - if (proto && proto->FillTClass(this)) { - fHasRootPcmInfo = kTRUE; - } + if (proto) + proto->FillTClass(this); } if (!fHasRootPcmInfo && gInterpreter->CheckClassInfo(fName, /* autoload = */ kTRUE)) { gInterpreter->SetClassInfo(this); // sets fClassInfo pointer @@ -3591,16 +3590,11 @@ TList *TClass::GetListOfBases() if (fState == kHasTClassInit) { R__LOCKGUARD(gInterpreterMutex); - // NOTE: Add test to prevent redo if another thread has already done the work. - // if (!fHasRootPcmInfo) { - - // The bases are in our ProtoClass; we don't need the class info. - TProtoClass *proto = TClassTable::GetProtoNorm(GetName()); - if (proto && proto->FillTClass(this)) { - // Not sure this code is still needed - // R__ASSERT(kFALSE); - - fHasRootPcmInfo = kTRUE; + if (!fHasRootPcmInfo) { + // The bases are in our ProtoClass; we don't need the class info. + TProtoClass *proto = TClassTable::GetProtoNorm(GetName()); + if (proto && proto->FillTClass(this)) + return fBase; } } // We test again on fCanLoadClassInfo has another thread may have executed it. @@ -3608,7 +3602,8 @@ TList *TClass::GetListOfBases() LoadClassInfo(); } } - if (!fClassInfo) return 0; + if (!fClassInfo) + return nullptr; if (!gInterpreter) Fatal("GetListOfBases", "gInterpreter not initialized"); @@ -3699,18 +3694,10 @@ TList *TClass::CreateListOfDataMembers(std::atomic &data, T if (!data) { if (fCanLoadClassInfo && fState == kHasTClassInit) { - // NOTE: Add test to prevent redo if another thread has already done the work. - // if (!fHasRootPcmInfo) { - // The members are in our ProtoClass; we don't need the class info. TProtoClass *proto = TClassTable::GetProtoNorm(GetName()); - if (proto && proto->FillTClass(this)) { - // Not sure this code is still needed - // R__ASSERT(kFALSE); - - fHasRootPcmInfo = kTRUE; + if (proto && proto->FillTClass(this)) return data; - } } data = new TListOfDataMembers(this, selection); @@ -5892,14 +5879,18 @@ void TClass::PostLoadCheck() } //////////////////////////////////////////////////////////////////////////////// -/// Set TObject::fBits and fStreamerType to cache information about the -/// class. The bits are +/// Returns the properties of the TClass as a bit field stored as a `Long_t` value. +/// +/// The bit values used for the return value are defined in the enum EProperty (in TDictionary.h) +/// +/// Also sets `TObject::fBits` and `fStreamerType` to cache information about the +/// class. The bits stored in `TObject::fBits` are /// ~~~ {.cpp} /// kIsTObject : the class inherits from TObject /// kStartWithTObject: TObject is the left-most class in the inheritance tree /// kIsForeign : the class doe not have a Streamer method /// ~~~ -/// The value of fStreamerType are +/// The value of `fStreamerType` are /// ~~~ {.cpp} /// kTObject : the class inherits from TObject /// kForeign : the class does not have a Streamer method @@ -5907,6 +5898,9 @@ void TClass::PostLoadCheck() /// kExternal: the class has a free standing way of streaming itself /// kEmulatedStreamer: the class is missing its shared library. /// ~~~ +/// +/// Implementation note: the data member fProperty has the value -1 +/// until it is initialized. Long_t TClass::Property() const { diff --git a/core/meta/src/TProtoClass.cxx b/core/meta/src/TProtoClass.cxx index 8861a21fbefed..4c3586419b175 100644 --- a/core/meta/src/TProtoClass.cxx +++ b/core/meta/src/TProtoClass.cxx @@ -26,6 +26,7 @@ Persistent version of a TClass. #include "TListOfEnums.h" #include "TRealData.h" #include "TError.h" +#include "TVirtualCollectionProxy.h" #include #include @@ -120,6 +121,12 @@ TProtoClass::TProtoClass(TClass* cl): // Info("TProtoClass","And is transient"); // } // } + } else if (cl->GetCollectionProxy()->GetProperties() & TVirtualCollectionProxy::kIsEmulated) { + // The collection proxy is emulated has the wrong size. + if (cl->HasInterpreterInfo()) + fSizeof = gCling->ClassInfo_Size(cl->GetClassInfo()); + else + fSizeof = -1; } cl->CalculateStreamerOffset(); @@ -161,12 +168,28 @@ void TProtoClass::Delete(Option_t* opt /*= ""*/) { /// duplicate dictionary is acceptable for namespace or STL collections. Bool_t TProtoClass::FillTClass(TClass* cl) { - if (cl->fRealData || cl->fBase.load() || cl->fData || cl->fEnums.load() || cl->fSizeof != -1 || cl->fCanSplit >= 0 || + if (cl->fRealData || cl->fBase.load() || cl->fData.load() || cl->fEnums.load() || cl->fCanSplit >= 0 || cl->fProperty != (-1)) { + if (cl->fState == TClass::kHasTClassInit) + // The class has dictionary, has gone through some initialization and is now being requested + // to be filled by a TProtoClass. + // This can be due to: + // (a) A duplicate dictionary for a class (with or without a rootpcm associated with) + // (b) The TClass was created before the registration of the rootpcm ** and ** it was + // attempted to be used before this registration + + // This is technically an error + // but we either already warned that there is a 2nd dictionary for the class (in TClassTable::Add) + // or this is the same (but now emptied) TProtoClass instance as before. + // We return false, since we are doing no actual change to the TClass instance and thus + // if a caller was hoping for 'improvement' in the state of the TClass instance, it did not + // happen. + return kFALSE; + if (cl->GetCollectionType() != ROOT::kNotSTL) { // We are in the case of collection, duplicate dictionary are allowed - // (and even somewhat excepted since they can be auto asked for). + // (and even somewhat expected since they can be auto asked for). // They do not always have a TProtoClass for them. In particular // the one that are pre-generated in the ROOT build (in what was once // called the cintdlls) do not have a pcms, neither does vector @@ -175,7 +198,7 @@ Bool_t TProtoClass::FillTClass(TClass* cl) { Info("FillTClass", "Returning w/o doing anything. %s is a STL collection.",cl->GetName()); return kFALSE; } - if (cl->Property() & kIsNamespace) { + if (cl->fProperty != -1 && (cl->fProperty & kIsNamespace)) { if (gDebug > 0) Info("FillTClass", "Returning w/o doing anything. %s is a namespace.",cl->GetName()); return kFALSE; @@ -183,6 +206,9 @@ Bool_t TProtoClass::FillTClass(TClass* cl) { Error("FillTClass", "TClass %s already initialized!", cl->GetName()); return kFALSE; } + if (cl->fHasRootPcmInfo) { + Fatal("FillTClass", "Filling TClass %s a second time but none of the info is in the TClass instance ... ", cl->GetName()); + } if (gDebug > 1) Info("FillTClass","Loading TProtoClass for %s - %s",cl->GetName(),GetName()); if (fPRealData.size() > 0) { @@ -247,7 +273,12 @@ Bool_t TProtoClass::FillTClass(TClass* cl) { cl->fEnums = temp; } - cl->fSizeof = fSizeof; + if (cl->fSizeof != -1 && cl->fSizeof != fSizeof) { + Error("FillTClass", + "For %s the sizeof provided by GenerateInitInstance (%d) is different from the one provided by TProtoClass (%d)", + cl->GetName(), cl->fSizeof, fSizeof); + } else + cl->fSizeof = fSizeof; cl->fCheckSum = fCheckSum; cl->fCanSplit = fCanSplit; cl->fProperty = fProperty; @@ -347,6 +378,7 @@ Bool_t TProtoClass::FillTClass(TClass* cl) { // delete fPRealData; // fPRealData = 0; + cl->fHasRootPcmInfo = kTRUE; return kTRUE; }