public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug c++/104577] New: needs copy constructor to call method of class non-type template parameter
@ 2022-02-17  3:55 f.heckenbach@fh-soft.de
  2022-02-18  1:14 ` [Bug c++/104577] needs copy constructor for " pinskia at gcc dot gnu.org
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: f.heckenbach@fh-soft.de @ 2022-02-17  3:55 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104577

            Bug ID: 104577
           Summary: needs copy constructor to call method of class
                    non-type template parameter
           Product: gcc
           Version: 11.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: f.heckenbach@fh-soft.de
  Target Milestone: ---

% cat test.cpp
struct S
{
  constexpr S () = default;
  S (S &&) = delete;
  operator int () const { return 0; }
};

template <S s> int i = s;

int main ()
{
  return i <S { }>;
}
% g++ -std=c++20 test.cpp
test.cpp:8:20: error: use of deleted function 'constexpr S::S(const S&)'
test.cpp:1:8: note: 'constexpr S::S(const S&)' is implicitly declared as
deleted because 'S' declares a move constructor or move assignment operator

I don't see why a copy constructor should be needed here. (clang accepts it.)

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Bug c++/104577] needs copy constructor for class non-type template parameter
  2022-02-17  3:55 [Bug c++/104577] New: needs copy constructor to call method of class non-type template parameter f.heckenbach@fh-soft.de
@ 2022-02-18  1:14 ` pinskia at gcc dot gnu.org
  2022-12-26  4:19 ` mail at jhellings dot nl
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: pinskia at gcc dot gnu.org @ 2022-02-18  1:14 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104577

Andrew Pinski <pinskia at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|UNCONFIRMED                 |NEW
   Last reconfirmed|                            |2022-02-18
            Summary|needs copy constructor to   |needs copy constructor for
                   |call method of class        |class non-type template
                   |non-type template parameter |parameter
     Ever confirmed|0                           |1

--- Comment #1 from Andrew Pinski <pinskia at gcc dot gnu.org> ---
Confirmed, it is for more than just calling a method on the non-type template
argument even.
Here is testcase which shows that:
struct S
{
  constexpr S () = default;
  constexpr S (S &&) = delete;
  constexpr S (const S &) = delete;
};

template <S s> 
struct i
{
    int y = 1;
};

int main ()
{
  return i <S { }>{}.y;
}

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Bug c++/104577] needs copy constructor for class non-type template parameter
  2022-02-17  3:55 [Bug c++/104577] New: needs copy constructor to call method of class non-type template parameter f.heckenbach@fh-soft.de
  2022-02-18  1:14 ` [Bug c++/104577] needs copy constructor for " pinskia at gcc dot gnu.org
@ 2022-12-26  4:19 ` mail at jhellings dot nl
  2022-12-28  1:30 ` f.heckenbach@fh-soft.de
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: mail at jhellings dot nl @ 2022-12-26  4:19 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104577

mail at jhellings dot nl changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |mail at jhellings dot nl

--- Comment #2 from mail at jhellings dot nl ---
I looked a bit further into this and into what the standard says. GCC does
partially the correct thing in this case, whereas several other compilers do
the wrong thing. See https://jhellings.nl/article?articleid=1 for the full
analysis.

The short summary:
In Clause 8 of Section [temp.param], the standard defines the value of a
non-type template argument:
"An id-expression naming a non-type template-parameter of class type T denotes
a static storage duration object of type const T known as a template parameter
object, whose value is that of the corresponding template argument after it has
been converted to the type of the template-parameter. ..."

Hence, whatever is provided as a non-type template parameter argument (of type
S in this bug report) is converted to the type S and the value resulting from
this conversion is available within the template as an lvalue object of type
const S.

To convert an expression to type S, you either need a constexpr copy
constructor (general case) or a constexpr move constructor (in the special case
in which you provide a movable value). 

Note that both Clang and Microsoft C++ do not correctly implement the semantics
of non-type template parameters (they pass values without converting them to
the type of the non-type template parameter).


I did find a separate issue, however:

/*
 * @author{Jelle Hellings}.
 * @copyright{The 2-Clause BSD License; see the end of this article}.
 */


/*
 * A type that can only be default-constructed and moved.
 */
struct no_copy
{
    /*
     * We can default-construct a dummy.
     */
    constexpr no_copy() {};

    /*
     * We cannot copy dummy.
     */
    no_copy(const no_copy&) = delete;

    /*
     * But we certainly can move a dummy.
     */
    constexpr no_copy(no_copy&&) {}
};



/*
 * A template function that accepts a no_copy non-type template parameter, but
 * does not do anything with it.
 */
template<no_copy NC> 
void test_f()
{
    /* We cannot pass NC to another template, as we do not have a copy
     * constructor. We can use this template by moving in a no_copy, however.
*/
};


/*
 * A template struct that accepts a no_copy non-type template parameter, but
 * does not do anything with it.
 */
template<no_copy NC> 
struct test_t
{
    /* We cannot pass NC to another template, as we do not have a copy
     * constructor. We can use this template by moving in a no_copy, however.
*/
};


/*
 * Entry-point of the program.
 */
int main ()
{
    test_f<no_copy{}>();     // Works fine, as it should.
    test_t<no_copy{}> value; // <- error: use of deleted function.
}

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Bug c++/104577] needs copy constructor for class non-type template parameter
  2022-02-17  3:55 [Bug c++/104577] New: needs copy constructor to call method of class non-type template parameter f.heckenbach@fh-soft.de
  2022-02-18  1:14 ` [Bug c++/104577] needs copy constructor for " pinskia at gcc dot gnu.org
  2022-12-26  4:19 ` mail at jhellings dot nl
@ 2022-12-28  1:30 ` f.heckenbach@fh-soft.de
  2023-02-11  4:35 ` herring at lanl dot gov
  2023-06-05 19:29 ` ppalka at gcc dot gnu.org
  4 siblings, 0 replies; 6+ messages in thread
From: f.heckenbach@fh-soft.de @ 2022-12-28  1:30 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104577

--- Comment #3 from Frank Heckenbach <f.heckenbach@fh-soft.de> ---
(In reply to mail from comment #2)

> I looked a bit further into this and into what the standard says. GCC does
> partially the correct thing in this case, whereas several other compilers do
> the wrong thing. See https://jhellings.nl/article?articleid=1 for the full
> analysis.

I'm not a standard expert. However, I don't think your analysis applies to
quite the same situation. Your test code uses two templates with non-type
parameters, test_cases and print_nttp, and I think the critical copy occurs at
the point where the value is passed from one to the other. If I avoid the
latter template by turning it into "void print_nttp(const copy_counter &C)" and
adjusting the callers accordingly, GCC outputs "0 0 0 0 1" as I'd expect.

> The short summary:
> In Clause 8 of Section [temp.param], the standard defines the value of a
> non-type template argument:
> "An id-expression naming a non-type template-parameter of class type T
> denotes a static storage duration object of type const T known as a template
> parameter object, whose value is that of the corresponding template argument
> after it has been converted to the type of the template-parameter. ..."
> 
> Hence, whatever is provided as a non-type template parameter argument (of
> type S in this bug report) is converted to the type S and the value
> resulting from this conversion is available within the template as an lvalue
> object of type const S.
> 
> To convert an expression to type S, you either need a constexpr copy
> constructor (general case) or a constexpr move constructor (in the special
> case in which you provide a movable value). 
(which you then demonstrate using static_cast)

As I said I'm not a standard expert, but if that's so, shouldn't this program
also require a copy/move constructor? GCC accepts it without one.

struct S
{
  constexpr S () = default;
  S (S &&) = delete;
};

int main ()
{
  static_cast <S> (S{});
}

But let's assume it does require one. This would then indeed apply to my
original example which uses "S{}" as the template argument (which then may need
to be copied/moved), but it wouldn't apply if I change it to just "i <{}>", or
to an int such as this:

struct S
{
  constexpr S (int) { }
  S (S &&) = delete;
  operator int () const { return 0; }
};

template <S s> int i = s;

int main ()
{
  return i <0>;
}

Now the template argument is either an empty braced-list or the integer 0 and
converting it to S does not require a copy/move constructor, but rather the
default constructor or the constructor taking an int, respectively. Still GCC
requires a copy/move constructor.

Further notes:

- If converting implies copying as you say, shouldn't GCC give 2 in the last
row? First you make an explicit copy (having count 1), then it's converted to
the argument of print_nttp which should yield in count 2, shouldn't it?

- The list of possible explanations given under "Dissecting the issue of
copy_counter" is not exhaustive:

* As you briefly mention in the text below, possible changes in the standard
between what the given compiler versions implement could be another explanation
(which may be relevant since the feature is rather new and the respective
standards have not been fully implemented by some or all of the given
compilers).

* Also possibly relevant, there's also the possibility that the program is
correct, but the standard does allow different results. I don't know and don't
claim that it is the case here, but I know there are cases where a constructor
must be available, but may or may not be actually called (copy elision). So
testing whether it is called may not not necessarily give the same answer as
finding out whether a constructor must be available.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Bug c++/104577] needs copy constructor for class non-type template parameter
  2022-02-17  3:55 [Bug c++/104577] New: needs copy constructor to call method of class non-type template parameter f.heckenbach@fh-soft.de
                   ` (2 preceding siblings ...)
  2022-12-28  1:30 ` f.heckenbach@fh-soft.de
@ 2023-02-11  4:35 ` herring at lanl dot gov
  2023-06-05 19:29 ` ppalka at gcc dot gnu.org
  4 siblings, 0 replies; 6+ messages in thread
From: herring at lanl dot gov @ 2023-02-11  4:35 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104577

S. Davis Herring <herring at lanl dot gov> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |herring at lanl dot gov

--- Comment #4 from S. Davis Herring <herring at lanl dot gov> ---
The understood direction for CWG2459 makes it clear that class-type template
parameters must indeed be copied; in fact, they have to be copied twice in some
cases to guarantee that they have the correct value when user-provided
constructors are involved.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Bug c++/104577] needs copy constructor for class non-type template parameter
  2022-02-17  3:55 [Bug c++/104577] New: needs copy constructor to call method of class non-type template parameter f.heckenbach@fh-soft.de
                   ` (3 preceding siblings ...)
  2023-02-11  4:35 ` herring at lanl dot gov
@ 2023-06-05 19:29 ` ppalka at gcc dot gnu.org
  4 siblings, 0 replies; 6+ messages in thread
From: ppalka at gcc dot gnu.org @ 2023-06-05 19:29 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104577

Patrick Palka <ppalka at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |ppalka at gcc dot gnu.org

--- Comment #5 from Patrick Palka <ppalka at gcc dot gnu.org> ---
The comment #1 and comment #2 testcases are (correctly?) accepted by GCC 13
ever since r13-1045-gcb7fd1ea85feea.  The original testcase is still rejected.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-06-05 19:29 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-17  3:55 [Bug c++/104577] New: needs copy constructor to call method of class non-type template parameter f.heckenbach@fh-soft.de
2022-02-18  1:14 ` [Bug c++/104577] needs copy constructor for " pinskia at gcc dot gnu.org
2022-12-26  4:19 ` mail at jhellings dot nl
2022-12-28  1:30 ` f.heckenbach@fh-soft.de
2023-02-11  4:35 ` herring at lanl dot gov
2023-06-05 19:29 ` ppalka at gcc dot gnu.org

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).