root icon indicating copy to clipboard operation
root copied to clipboard

`TEnum::GetEnum` does not seem to see 'through' using statements.

Open pcanal opened this issue 1 year ago • 2 comments

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

pcanal avatar May 02 '24 19:05 pcanal

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).

pcanal avatar May 02 '24 19:05 pcanal

So there is 2 solutions:

  1. Update the semantic of TClass::GetListOfEnums to also include the indirect declaration (relaxing/removing the call to ClassInfo_Contains)
  2. Update TEnum::GetEnum to first do a name normalization on the input (likely the right solution but we need to be careful in regard to autoloading (see TClass::GetClass).

pcanal avatar May 02 '24 20:05 pcanal