[llvm-bugs] [Bug 42513] New: Friend function declarations hidden in qualifed name lookup (regression introduced by commit r350505)

via llvm-bugs llvm-bugs at lists.llvm.org
Thu Jul 4 20:00:11 PDT 2019


https://bugs.llvm.org/show_bug.cgi?id=42513

            Bug ID: 42513
           Summary: Friend function declarations hidden in qualifed name
                    lookup (regression introduced by commit r350505)
           Product: clang
           Version: trunk
          Hardware: PC
                OS: Linux
            Status: NEW
          Severity: enhancement
          Priority: P
         Component: C++
          Assignee: unassignedclangbugs at nondot.org
          Reporter: andyg1001 at hotmail.co.uk
                CC: blitzrakete at gmail.com, dgregor at apple.com,
                    erik.pilkington at gmail.com, llvm-bugs at lists.llvm.org,
                    richard-llvm at metafoo.co.uk

Commit r350505 (PR38883) introduced a regression for the following code:

/*******************************************************************/
#include <stdio.h>

struct Widget {
  virtual ~Widget() = default;
  virtual void doAction() const = 0;

  template <typename X, typename... Args>
  static Widget* Construct(Args... args) {
    constexpr auto i = GetWidgetCtor<X>(0);
    if constexpr (i != nullptr)
      return i(args...);
    else
      return nullptr;
  }

private:
  template <typename X, auto Ret = WidgetCtor((X*)nullptr)>
  static constexpr auto GetWidgetCtor(X* = nullptr) {
    return Ret;
  }

  template <typename X>
  static constexpr auto GetWidgetCtor(...) {
    return nullptr;
  }
};

template <typename... Args>
using WidgetGenerator = Widget*(*)(Args...);


template <typename X, typename... Args>
struct StandardWidget : Widget {
  virtual void doAction() const override {
    x.doAction();
  }

private:
  friend constexpr WidgetGenerator<Args...> WidgetCtor(X*) {
    return [](Args... args){
        return static_cast<Widget*>(new StandardWidget(X(args...)));
      };
  }

  StandardWidget(X x) : x(x) { }
  X x;
};


struct X1 {
  friend constexpr WidgetGenerator<> WidgetCtor(X1*);

  X1() { }

  void doAction() const {
    puts("X1::doAction");
  }
};

struct X2 {
  friend constexpr WidgetGenerator<int> WidgetCtor(X2*);

  X2(int i) : i(i) { }

  void doAction() const {
    printf("X2::doAction with %i\n", i);
  }

  int i;
};

struct X3 {
  friend constexpr WidgetGenerator<> WidgetCtor(X3*);

  X3(double d) : d(d) { }
  double d;
};


template struct StandardWidget<X1>;
template struct StandardWidget<X2, int>;

struct X3Widget : Widget {
  virtual void doAction() const override {
    printf("X3::doAction with %f\n", x.d);
  }

private:
  friend constexpr WidgetGenerator<> WidgetCtor(X3*) {
    return [](){ 
        return static_cast<Widget*>(new X3Widget());
      };
  }

  X3Widget() : x(12.5) { }
  X3 x;
};


void test() {
  if (Widget* i = Widget::Construct<X1>())
    i->doAction(), delete i;  // "X1::doAction"
  if (Widget* i = Widget::Construct<X2>(123))
    i->doAction(), delete i;  // "X2::doAction with 123"
  if (Widget* i = Widget::Construct<X3>())
    i->doAction(), delete i;  // "X3::doAction with 12.5"
  if (Widget* i = Widget::Construct<int>())
    i->doAction(), delete i;  // no-op
}
/*******************************************************************/


Rather than outputting three lines, only one is now output ("X3::doAction with
12.5").  The code compiled correctly on clang 7 and earlier and also compiles
correctly on gcc.

The relevant output (-S -emit-llvm -O2) for the test() function in clang 8 and
later is:


define dso_local void @_Z4testv() local_unnamed_addr #0 personality i32 (...)*
@__gxx_personality_v0 !dbg !135 {
  %1 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([22 x i8],
[22 x i8]* @.str.2, i64 0, i64 0), double 1.250000e+01), !dbg !147
  ret void, !dbg !174
}


And for clang 7 and earlier is:


define dso_local void @_Z4testv() local_unnamed_addr #0 personality i32 (...)*
@__gxx_personality_v0 !dbg !136 {
  %1 = tail call i32 @puts(i8* getelementptr inbounds ([13 x i8], [13 x i8]*
@.str, i64 0, i64 0)), !dbg !148
  %2 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([22 x i8],
[22 x i8]* @.str.1, i64 0, i64 0), i32 123), !dbg !151
  %3 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([22 x i8],
[22 x i8]* @.str.2, i64 0, i64 0), double 1.250000e+01), !dbg !154
  ret void, !dbg !181
}


In the PR38883, I posted the above code and the following trivial fix:


Index: lib/AST/DeclBase.cpp
===================================================================
--- lib/AST/DeclBase.cpp        (revision 350505)
+++ lib/AST/DeclBase.cpp        (working copy)
@@ -1407,7 +1407,8 @@

   // Skip friends and local extern declarations unless they're the first
   // declaration of the entity.
-  if ((D->isLocalExternDecl() || D->getFriendObjectKind()) &&
+  if ((D->isLocalExternDecl() || 
+       D->getFriendObjectKind() == Decl::FOK_Declared) &&
       D != D->getCanonicalDecl())
     return true;


Richard Smith commented on the above that "The proposed fix doesn't seem
problematic, but only changes which declaration of the friend will be in the
DeclContext name lookup table ... we should walk its redeclarations and find
the one with the body" but unfortunately I am not able to work out the best
method to do this, so I have posted this bug to enable someone else to take up
the burden if they can see a good solution :o)

-- 
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20190705/f3b9a529/attachment.html>


More information about the llvm-bugs mailing list