template specialization and redecls

I'm seeing behavior that mystifies me. Can someone explain it?

Consider this code:

I'm seeing behavior that mystifies me. Can someone explain it?

Consider this code:
---
struct B {
template<class T> class C;
template<class T> friend class C;
//friend class C<float>;
};

template<class T> class C {
};

C<int>* foo;
---

When I look at the CXXRecordDecl for C<int> (on the last line), and
look at all the redecls (from redecls_begin() to redecls_end()), I see
only one redecl, for the definition of class C on line 7.

But if I uncomment line 4, suddenly I see 3 redecls: one for line 2,
one for line 3, and one for line 7.

Why would adding friendship on a specialization version of C cause the
other items in B to become a redecl of C?

That sounds very broken; are B::C and ::C somehow getting tangled together?

Also, what is the 'proper' contents of the redecls of C? Should it
only include declarations that are visible at the 'C<int>* foo' line?
That is, the behavior when line 4 is commented out is correct, and the
behavior when I uncomment line 4 is incorrect?

Line 4 shouldn't have any effect on ::C<int>.

  - Doug

} That sounds very broken; are B::C and ::C somehow getting tangled
} together?

I don't think so. The same problem is in evidence even with the
following code (where there's no B::C):

  struct B {
   template<class T> friend class C;
   friend class C<float>;
  };
  
  template<class T> class C {
  };
  
  C<int>* foo;

I agree it seems very broken. :slight_smile:

craig

I wouldn't be surprised at all if we had problems with the redeclaration chain for 'C' in this case. There are a number of bugs relating to redeclarations and friends where, e.g., friend declarations don't show up in the redeclaration chain when they should. It's at the point where we need to implement a completely different approach to finding friends. Along the way, it'd be nice to get redeclaration chains that handle locally-scoped extern declarations, e.g.,

  void f() {
    extern int i;
  }

  void g() {
    extern int i;
  }

We have a hack to handle this in C, but it needs to be fixed for C++ and generalized to also support friends, which have the same kind of visible-for-redeclaration-but-not-for-normal-lookup issues.

  - Doug

} e.g., friend declarations don't show up in the redeclaration chain
} when they should.

When might a friend declaration have a redeclarations in the same
DeclContext? I'm sure it's possible, I just can't think of how.

} It's at the point where we need to implement a completely different
} approach to finding friends.

Sounds like it's above my paygrade. :slight_smile:

This bug is causing me a small bit of trouble for
include-what-you-use. One easy way around it, for me, would be if I
could tell, when traversing redecls, whether a particular declaration
was part of a friend declaration or not. Is there anything that would
tell me that?

craig

} e.g., friend declarations don't show up in the redeclaration chain
} when they should.

When might a friend declaration have a redeclarations in the same
DeclContext? I'm sure it's possible, I just can't think of how.

They won't occur in the same (lexical) DeclContext; they'll all be in the same semantic DeclContext, because they're declaring the same entity.

} It's at the point where we need to implement a completely different
} approach to finding friends.

Sounds like it's above my paygrade. :slight_smile:

It's a nontrivial amount of work. I can elaborate if anyone feels like tackling this :wink:

This bug is causing me a small bit of trouble for
include-what-you-use. One easy way around it, for me, would be if I
could tell, when traversing redecls, whether a particular declaration
was part of a friend declaration or not. Is there anything that would
tell me that?

IIRC, it's Decl::getFriendObjectKind().

  - Doug