`TEnum::GetEnum` does not seem to see 'through' using statements.
Check duplicate issues.
- [ ] Checked for duplicates
Description
As first seen in https://github.com/cms-sw/cmssw/issues/44670, TEnum::GetEnum does not seem to see 'through' using statements.
root [1] namespace A { enum E { kOne }; }
root [2] namespace B { using A::E; }
root [3] TEnum::GetEnum("A::E")
(TEnum *) 0x600000cc9770
root [4] TEnum::GetEnum("B::E")
(TEnum *) nullptr
root [5] namespace C { using namespace A; }
root [6] TEnum::GetEnum("C::E")
(TEnum *) nullptr
Reproducer
With file enumusing.cxx (see below) return 1 instead 0;
The issue was uncovered was investigating the change/improvement shown in related_enumusing where the return value on master and v6-32 release candidate (due to commit fa278745f2141ea02d40ed27af986ac8729dee2b), the execution now return 0 (new correct behavior) instead of 3 (old inaccurate behavior but expected by the user).
namespace reco
{
namespace btau
{
enum class TaggingVariableName {
kOne
};
} // namespace btau
using btau::TaggingVariableName;
struct TaggingVariableList {
bool get(TaggingVariableName in) { return true; };
};
} // namespace reco
int enumusing()
{
auto e1 = TEnum::GetEnum("reco::TaggingVariableName");
if (!e1)
return 1;
auto e2 = TEnum::GetEnum("reco::btau::TaggingVariableName");
if (!e2)
return 2;
return 0;
}
int related_enumusing()
{
auto c = TClass::GetClass("reco::TaggingVariableList");
auto m = static_cast<TMethod*>(c->GetListOfMethods()->FindObject("get"));
if (!m)
return 1;
auto arg = static_cast<TMethodArg*>(m->GetListOfMethodArgs()->At(0));
if (!arg)
return 2;
std::string target("reco::btau::TaggingVariableName");
std::string newtype("reco::TaggingVariableName");
if (newtype == arg->GetTypeName())
return 0;
if (target == arg->GetTypeName())
return 3;
return 4;
}
ROOT version
master
Installation method
any
Operating system
any
Additional context
TClass::GetClass handles properly direct using statement
root [3] namespace A { class B {}; }
root [4] TClass::GetClass("A::B")
(TClass *) 0x13af817f0
root [5] namespace C { using A::B; }
root [6] TClass::GetClass("C::B")
(TClass *) 0x13af817f0
but not using namespace statement:
root [7] namespace D { using namespace A; }
root [8] TClass::GetClass("D::B")
(TClass *) nullptr
One of the parts/elements of the behavior is that:
cling::utils::Lookup::Tag(&fInterpreter->getSema(), name, dc);
with
name == "E" and dc == decl_for("B")
and with B:
NamespaceDecl 0x156e8b600 </Users/pcanal/root_working/test/2024-meta/test_usingenum.cxx:2:1, col:27> col:11 B
|-UsingDecl 0x156e8b6b0 <col:15, col:24> col:24 A::E
`-UsingShadowDecl 0x156e8b708 <col:24> col:24 implicit Enum 0x156e8b4c8 'E'
`-EnumType 0x156e8b590 'A::E'
`-Enum 0x156e8b4c8 'E'
returns the E decl (i.e. ignores the UsingShadowDecl):
(lldb) p R.getFoundDecl()->dump()
EnumDecl 0x156e8b4c8 </Users/pcanal/root_working/test/2024-meta/test_usingenum.cxx:1:15, col:29> col:20 E
`-EnumConstantDecl 0x156e8b5b0 <col:24> col:24 kOne 'A::E'
later in the TEnum::Get process:
* frame #0: 0x000000011858a084 libCling.so`TCling::ClassInfo_Contains(this=0x0000000155f063e0, info=0x0000600000540e10, declid=0x0000000156e8b4c8) const at TCling.cxx:8147:11
frame #1: 0x000000010114ba64 libCore.so`TListOfEnums::Get(this=0x0000600000b5bac0, id=0x0000000156e8b4c8, name="E") at TListOfEnums.cxx:240:26
frame #2: 0x000000010114b8d8 libCore.so`TListOfEnums::FindObject(this=0x0000600000b5bac0, name="E") const at TListOfEnums.cxx:196:60
TCling::ClassInfo_Contains rejects the enum since:
return declDC->Equals(ctxt);
is false since declDC point to A (the actual declaration context of the enum) and ctxt is B (the namespace we started the search in).
So there is 2 solutions:
- Update the semantic of
TClass::GetListOfEnumsto also include the indirect declaration (relaxing/removing the call toClassInfo_Contains) - Update
TEnum::GetEnumto first do a name normalization on the input (likely the right solution but we need to be careful in regard to autoloading (seeTClass::GetClass).