Bug 95197 - libgomp/testsuite/libgomp.c++/for-27.C fails with -std=c++17
Summary: libgomp/testsuite/libgomp.c++/for-27.C fails with -std=c++17
Status: ASSIGNED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: unknown
: P3 normal
Target Milestone: ---
Assignee: Jakub Jelinek
URL:
Keywords: openmp
Depends on:
Blocks:
 
Reported: 2020-05-19 00:14 UTC by Marek Polacek
Modified: 2020-06-14 10:07 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2020-05-25 00:00:00


Attachments
gcc11-pr95197.patch (2.25 KB, patch)
2020-05-25 16:55 UTC, Jakub Jelinek
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Marek Polacek 2020-05-19 00:14:11 UTC
See <https://gcc.gnu.org/pipermail/gcc-patches/2020-May/545879.html> for why this fails.  My workaround was

--- a/libgomp/testsuite/libgomp.c++/for-27.C
+++ b/libgomp/testsuite/libgomp.c++/for-27.C
@@ -151,6 +151,12 @@ f4 (const I<int> &x, const I<int> &y)
     else if (results[i])				\
       abort ()
 
+// FIXME: OpenMP seems to count on this being instantiated; the copy
+// constructor is called in functions fn2/fn3/fn4.  But in C++17 we
+// elide this copy constructor, so it's never been instantiated.  For
+// now instantiate it explicitly.
+template I<int>::I(const I<int> &);
+
 int
 main ()
 {

Seems that OpenMP has to call mark_used or some such.
Comment 1 Jakub Jelinek 2020-05-25 14:23:40 UTC
For e.g.
void bar (I<int> &a);
  
void
foo (I<int> &a)
{
  #pragma omp task //firstprivate (a)
  bar (a);
}
with the same templates this is handled by omp_cxx_notice_variable, which will
1068		      get_copy_ctor (type, tf_none);
1069		      get_dtor (type, tf_none);
because when it is done only during gimplification, we don't instantiate it anymore.
So, I think I need to go through all the lang_hooks.decls.omp_finish_clause
calls in the gimplifier and deal with it similarly during genericization time.
Comment 2 Jakub Jelinek 2020-05-25 14:38:21 UTC
Reduced testcase:

// { dg-do link }

typedef __PTRDIFF_TYPE__ ptrdiff_t;

template <typename T>
class I
{
public:
  typedef ptrdiff_t difference_type;
  I ();
  ~I ();
  I (T *);
  I (const I &);
  T &operator * ();
  T *operator -> ();
  T &operator [] (const difference_type &) const;
  I &operator = (const I &);
  I &operator ++ ();
  I operator ++ (int);
  I &operator -- ();
  I operator -- (int);
  I &operator += (const difference_type &);
  I &operator -= (const difference_type &);
  I operator + (const difference_type &) const;
  I operator - (const difference_type &) const;
  template <typename S> friend bool operator == (I<S> &, I<S> &);
  template <typename S> friend bool operator == (const I<S> &, const I<S> &);
  template <typename S> friend bool operator < (I<S> &, I<S> &);
  template <typename S> friend bool operator < (const I<S> &, const I<S> &);
  template <typename S> friend bool operator <= (I<S> &, I<S> &);
  template <typename S> friend bool operator <= (const I<S> &, const I<S> &);
  template <typename S> friend bool operator > (I<S> &, I<S> &);
  template <typename S> friend bool operator > (const I<S> &, const I<S> &);
  template <typename S> friend bool operator >= (I<S> &, I<S> &);
  template <typename S> friend bool operator >= (const I<S> &, const I<S> &);
  template <typename S> friend typename I<S>::difference_type operator - (I<S> &, I<S> &);
  template <typename S> friend typename I<S>::difference_type operator - (const I<S> &, const I<S> &);
  template <typename S> friend I<S> operator + (typename I<S>::difference_type , const I<S> &);
private:
  T *p;
};
template <typename T> I<T>::I () : p (0) {}
template <typename T> I<T>::~I () {}
template <typename T> I<T>::I (T *x) : p (x) {}
template <typename T> I<T>::I (const I &x) : p (x.p) {}
template <typename T> T &I<T>::operator * () { return *p; }
template <typename T> T *I<T>::operator -> () { return p; }
template <typename T> T &I<T>::operator [] (const difference_type &x) const { return p[x]; }
template <typename T> I<T> &I<T>::operator = (const I &x) { p = x.p; return *this; }
template <typename T> I<T> &I<T>::operator ++ () { ++p; return *this; }
template <typename T> I<T> I<T>::operator ++ (int) { return I (p++); }
template <typename T> I<T> &I<T>::operator -- () { --p; return *this; }
template <typename T> I<T> I<T>::operator -- (int) { return I (p--); }
template <typename T> I<T> &I<T>::operator += (const difference_type &x) { p += x; return *this; }
template <typename T> I<T> &I<T>::operator -= (const difference_type &x) { p -= x; return *this; }
template <typename T> I<T> I<T>::operator + (const difference_type &x) const { return I (p + x); }
template <typename T> I<T> I<T>::operator - (const difference_type &x) const { return I (p - x); }
template <typename T> bool operator == (I<T> &x, I<T> &y) { return x.p == y.p; }
template <typename T> bool operator == (const I<T> &x, const I<T> &y) { return x.p == y.p; }
template <typename T> bool operator != (I<T> &x, I<T> &y) { return !(x == y); }
template <typename T> bool operator != (const I<T> &x, const I<T> &y) { return !(x == y); }
template <typename T> bool operator < (I<T> &x, I<T> &y) { return x.p < y.p; }
template <typename T> bool operator < (const I<T> &x, const I<T> &y) { return x.p < y.p; }
template <typename T> bool operator <= (I<T> &x, I<T> &y) { return x.p <= y.p; }
template <typename T> bool operator <= (const I<T> &x, const I<T> &y) { return x.p <= y.p; }
template <typename T> bool operator > (I<T> &x, I<T> &y) { return x.p > y.p; }
template <typename T> bool operator > (const I<T> &x, const I<T> &y) { return x.p > y.p; }
template <typename T> bool operator >= (I<T> &x, I<T> &y) { return x.p >= y.p; }
template <typename T> bool operator >= (const I<T> &x, const I<T> &y) { return x.p >= y.p; }
template <typename T> typename I<T>::difference_type operator - (I<T> &x, I<T> &y) { return x.p - y.p; }
template <typename T> typename I<T>::difference_type operator - (const I<T> &x, const I<T> &y) { return x.p - y.p; }
template <typename T> I<T> operator + (typename I<T>::difference_type x, const I<T> &y) { return I<T> (x + y.p); }

void
bar (I<int> &a)
{
}

void
foo (const I<int> &a, const I<int> &b)
{
  I<int> i;
  #pragma omp distribute parallel for
  for (i = a; i < b; i++)
    bar (i);
}

int
main ()
{
  int a[64];
  foo (&a[0], &a[63]);
}
Comment 3 Jakub Jelinek 2020-05-25 16:55:59 UTC
Created attachment 48597 [details]
gcc11-pr95197.patch

Untested fix.
Comment 4 GCC Commits 2020-05-26 07:39:15 UTC
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

https://gcc.gnu.org/g:f1f862aec2c3b93dbd6adfc35b0e1b6034e59c21

commit r11-629-gf1f862aec2c3b93dbd6adfc35b0e1b6034e59c21
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Tue May 26 09:35:21 2020 +0200

    openmp: Ensure copy ctor for composite distribute parallel for class iterators is instantiated [PR95197]
    
    During gimplification omp_finish_clause langhook is called in several places
    to add the language specific info to the clause like what default/copy ctors,
    dtors and assignment operators should be used.
    
    Unfortunately, if it refers to some not yet instantiated method, during
    gimplification it is too late and the methods will not be instantiated
    anymore.  For other cases, the genericizer has code to detect those and
    instantiate whatever is needed, this change adds the same for
    distribute parallel for class iterators where we under the hood need
    a copy constructor for the iterator to implement it.
    
    2020-05-26  Jakub Jelinek  <jakub@redhat.com>
    
            PR c++/95197
            * gimplify.c (find_combined_omp_for): Move to omp-general.c.
            * omp-general.h (find_combined_omp_for): Declare.
            * omp-general.c: Include tree-iterator.h.
            (find_combined_omp_for): New function, moved from gimplify.c.
    
            * cp-gimplify.c: Include omp-general.h.
            (cp_genericize_r) <case OMP_DISTRIBUTE>: For class iteration
            variables in composite distribute parallel for, instantiate copy
            ctor of their types.
Comment 5 GCC Commits 2020-06-14 10:07:26 UTC
The releases/gcc-10 branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

https://gcc.gnu.org/g:6d6df84dc031924cb95599a6c498aa27d3bceac4

commit r10-8294-g6d6df84dc031924cb95599a6c498aa27d3bceac4
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Tue May 26 09:35:21 2020 +0200

    openmp: Ensure copy ctor for composite distribute parallel for class iterators is instantiated [PR95197]
    
    During gimplification omp_finish_clause langhook is called in several places
    to add the language specific info to the clause like what default/copy ctors,
    dtors and assignment operators should be used.
    
    Unfortunately, if it refers to some not yet instantiated method, during
    gimplification it is too late and the methods will not be instantiated
    anymore.  For other cases, the genericizer has code to detect those and
    instantiate whatever is needed, this change adds the same for
    distribute parallel for class iterators where we under the hood need
    a copy constructor for the iterator to implement it.
    
    2020-05-26  Jakub Jelinek  <jakub@redhat.com>
    
            PR c++/95197
            * gimplify.c (find_combined_omp_for): Move to omp-general.c.
            * omp-general.h (find_combined_omp_for): Declare.
            * omp-general.c: Include tree-iterator.h.
            (find_combined_omp_for): New function, moved from gimplify.c.
    
            * cp-gimplify.c: Include omp-general.h.
            (cp_genericize_r) <case OMP_DISTRIBUTE>: For class iteration
            variables in composite distribute parallel for, instantiate copy
            ctor of their types.
    
    (cherry picked from commit f1f862aec2c3b93dbd6adfc35b0e1b6034e59c21)