[cfe-dev] Advice on patch
Nathan Sidwell via cfe-dev
cfe-dev at lists.llvm.org
Fri Apr 30 10:53:52 PDT 2021
On 4/30/21 12:55 PM, David Rector wrote:
>
>
>> On Apr 30, 2021, at 7:14 AM, Nathan Sidwell via cfe-dev
>> <cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>> wrote:
>>
>> hi, I've been working on p1099
>> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1099r5.html
>> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1099r5.html>)--
>> c++20's using-enum feature. Among other things it allows you to being
>> the enumerators of a scoped enumeration into the local scope 'using
>> enum maybe-qualified-tag-name ;' Brings the members of that enum in,
>> as if by individual using declarations. I posted an initial patch
>> seeking advice on a couple of issues as
>> https://reviews.llvm.org/D101370 <https://reviews.llvm.org/D101370>
>>
>> I chose to extend EnumDecl, rather than a new decl as we still need
>> the shadow decl chain. The enum is parsed with ParseEnumSpecifier.
>>
>> Q1) I need to add the EnumDecl to the UsingDecl, and don't need the
>> QualifiedLoc or DNLoc fields. Should I use a local union to overlay
>> those pieces of data, or just live with the size expansion?
>>
>
> My preference would be to have a separate UsingEnumDecl, and use the
> opportunity to factor out a common base to handle the shadow stuff, in a
> manner that would also provide a clearer interface for novice users.
> (`shadows()` etc. is a bit to abstruse for someone who just wants to
> quickly get the thing or things being "used.")
hehe, a vote for each direction -- my internal dilemma is externalized!
>
> IIUC a UsingDecl presently only ever has more than one shadow when it
> refers to an overloaded function. (And now, when it represents a
> using-enum-declaration with multiple enumerators).
Not quite. Consider
namespace Foo {
struct bob;
int bob;
}
using Foo::bob; // both struct tag and int var now visible!
>
> So, a structure like this might be clearer:
> ```
> /// A base of both UsingDecl and UsingEnumDecl
> class ShadowIntroducingDecl : public NamedDecl {
That's probably a good name -- put 'Shadow' in all the closely related tags.
> // All the shadow stuff from UsingDecl, e.g.:
> llvm::PointerIntPair<UsingShadowDecl *, 1, bool> FirstUsingShadow;
> public:
> shadow_range shadows() const;
> unsigned shadow_size() const;
> //...
> };
>
> class UsingDecl : public ShadowIntroducingDecl, public
> Mergeable<UsingDecl> {
> // ...
> // DNLoc, QualifierLoc, anything else specific to UsingDecls.
> public:
> // --- Non-essential methods provided for clarity --- //
> bool TargetIsFunction() const {
> return
> isa<FunctionDecl>(FirstUsingShadow.getPointer()->getTargetDecl());
> }
> NamedDecl *getTargetAsSingleDecl() {
> assert(!TargetIsFunction() && "use getTargetOverloads() instead");
> assert(shadow_size() == 1);
> return FirstUsingShadow.getPointer()->getTargetDecl();
> }
>
> /// Same as shadow_iterator, but dereferences directly to the target
> FunctionDecls
> struct function_overload_iterator : shadow_iterator {
> FunctionDecl *operator*() const { return
> cast<FunctionDecl>(shadow_iterator::operator*()->getTargetDecl()); }
> FunctionDecl *operator->() const //""
> //...
> };
> using function_overload_range =
> llvm::iterator_range<function_overload_iterator>;
>
> function_overload_range getTargetOverloads() const {
> assert(TargetIsFunction());
> return static_cast<function_overload_range>(shadows()); //or whatever
> }
> unsigned getNumOverloads() const {
> assert(TargetIsFunction());
> return shadow_size();
> }
> };
>
> class UsingEnumDecl : public ShadowIntroducingDecl, public
> Mergeable<UsingEnumDecl> {
> //...
> public:
> EnumDecl *getNominatedEnum() const {
> return
> cast<EnumDecl>(FirstShadowDecl.getPointer()->getTargetDecl()->getDeclContext());
>
> }
> auto getTargetEnumerators() const {
> return getNominatedEnum()->decls();
> }
> };
>
> ```
>
>> Q2) When the scoped enum is a template member, the members are
>> instantiated when one looks inside the enum -- not when the enum decl
>> is instantiated. That's done with RequireCompleteDeclContext, which
>> is designed for when the parser is looking, and therefore takes a
>> ScopeRef. This is a new situation where we don't have a ScopeRef, we
>> have an EnumDecl, which we desire to complete. Is it best to break
>> RequireCompleteDeclContext apart in some way so there's a new entry
>> point for the new use?
>>
>
> To me it seems reasonable to separate out a
> `RequireCompleteEnumDecl(EnumDecl *D, CXXScopeSpec *SS = nullptr)`
> function that handles just the enum-specific stuff in there.
good, agreed.
>
>> thanks,
>>
>> nathan
>>
>> --
>> Nathan Sidwell
>>
>
> One last thing while we’re on the general topic of UsingDecl, just in
> case you or anyone else wants to take this opportunity to clean it up
> more generally. There is presently no `UsingType` to provide the type
> sugar that a particular type was introduce via a UsingDecl. E.g.
>
> ```
> struct A {
> struct Inner {};
> };
> struct B : A {
> using A::Inner;
> Inner i;
> };
>
> The type dump of `B::i` is
>
> RecordType 0x7fe32c80c2c0 'struct A::Inner'
> `-CXXRecord 0x7fe32c80c210 'Inner'
>
> when I think it really should be something like
>
> UsingType *********** 'struct A::Inner' sugar
> `-RecordType 0x7fe32c80c2c0 'struct A::Inner'
> `-CXXRecord 0x7fe32c80c210 'Inner'
>
> I think this is may be the only case in the AST for which full source
> information about a type is not provided via sugar. And, a quick test
> suggests the UsingShadowDecl info is available during Lookup (i.e. we
> know if a particular lookup result is a UsingShadowDecl), so a UsingType
> would be straightforward to add.
>
>
>
--
Nathan Sidwell
More information about the cfe-dev
mailing list