On Tue, 25 Aug 2020, Patrick Palka wrote: > My original patch that implemented the calendar type operations failed > to enforce a constraint on some of the addition/subtraction operator > overloads that take a 'months' argument: > > Constraints: If the argument supplied by the caller for the months > parameter is convertible to years, its implicit conversion sequence to > years is worse than its implicit conversion sequence to months > > This constraint is relevant when adding/subtracting a duration to/from > say a year_month when the given duration is convertible to both 'months' > and to 'years'. The correct behavior here in light of this constraint > is to select the (more efficient) 'years'-taking overload, but we > currently emit an ambiguous overload error. > > This patch follows the approach taken in the 'date' library and defines > the constrained 'months'-taking operator overloads as function > templates, so that we break such a implicit-conversion tie by selecting > the non-template 'years'-taking overload. > > Tested on x86_64-pc-linux-gnu, does this look OK to commit? (The below > diff is generated with --ignore-space-change for easier review. In the > actual patch, the function templates are indented two extra spaces after > the template parameter list.) > > libstdc++-v3/ChangeLog: > > * include/std/chrono (__detail::__unspecified_month_disambuator): > Define.
Whoops, this ChangeLog line should say __detail::__months_years_conversion_disambiguator. > (year_month::operator+=): Turn the 'months'-taking overload > into a function template, so that the 'years'-taking overload is > selected in case of an equally-ranked implicit conversion > sequence to both 'months' and 'years' from the supplied argument. > (year_month::operator-=): Likewise. > (year_month::operator+): Likewise. > (year_month::operator-): Likewise. > (year_month_day::operator+=): Likewise. > (year_month_day::operator-=): Likewise. > (year_month_day::operator+): Likewise. > (year_month_day::operator-): Likewise. > (year_month_day_last::operator+=): Likewise. > (year_month_day_last::operator-=): Likewise. > (year_month_day_last::operator+): Likewise > (year_month_day_last::operator-): Likewise. > (year_month_day_weekday::operator+=): Likewise > (year_month_day_weekday::operator-=): Likewise. > (year_month_day_weekday::operator+): Likewise. > (year_month_day_weekday::operator-): Likewise. > (year_month_day_weekday_last::operator+=): Likewise > (year_month_day_weekday_last::operator-=): Likewise. > (year_month_day_weekday_last::operator+): Likewise. > (year_month_day_weekday_last::operator-): Likewise. > (testsuite/std/time/year_month/2.cc): New test. > (testsuite/std/time/year_month_day/2.cc): New test. > (testsuite/std/time/year_month_day_last/2.cc): New test. > (testsuite/std/time/year_month_weekday/2.cc): New test. > (testsuite/std/time/year_month_weekday_last/2.cc): New test. > --- > libstdc++-v3/include/std/chrono | 52 ++++++++++++++++++- > .../testsuite/std/time/year_month/2.cc | 40 ++++++++++++++ > .../testsuite/std/time/year_month_day/2.cc | 40 ++++++++++++++ > .../std/time/year_month_day_last/2.cc | 40 ++++++++++++++ > .../std/time/year_month_weekday/2.cc | 40 ++++++++++++++ > .../std/time/year_month_weekday_last/2.cc | 40 ++++++++++++++ > 6 files changed, 250 insertions(+), 2 deletions(-) > create mode 100644 libstdc++-v3/testsuite/std/time/year_month/2.cc > create mode 100644 libstdc++-v3/testsuite/std/time/year_month_day/2.cc > create mode 100644 libstdc++-v3/testsuite/std/time/year_month_day_last/2.cc > create mode 100644 libstdc++-v3/testsuite/std/time/year_month_weekday/2.cc > create mode 100644 > libstdc++-v3/testsuite/std/time/year_month_weekday_last/2.cc > > diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono > index 3cc1438a7b6..0e272c3da58 100644 > --- a/libstdc++-v3/include/std/chrono > +++ b/libstdc++-v3/include/std/chrono > @@ -2046,6 +2046,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > // YEAR_MONTH > > + namespace __detail > + { > + // [time.cal.ym], [time.cal.ymd], etc constrain the 'months'-taking > + // addition/subtraction operator overloads like so: > + // > + // Constraints: if the argument supplied by the caller for the months > + // parameter is convertible to years, its implicit conversion > sequence > + // to years is worse than its implicit conversion sequence to months. > + // > + // We realize this constraint by defining the 'months'-taking > overloads as > + // function templates (with a dummy defaulted template parameter), so > that > + // overload resolution doesn't select the 'months'-taking overload > unless > + // the implicit conversion sequence to 'months' is better than that to > + // 'years'. > + using __months_years_conversion_disambiguator = void; > + } > + > class year_month > { > private: > @@ -2068,6 +2085,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > month() const noexcept > { return _M_m; } > > + template<typename = __detail::__months_years_conversion_disambiguator> > constexpr year_month& > operator+=(const months& __dm) noexcept > { > @@ -2075,6 +2093,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return *this; > } > > + template<typename = __detail::__months_years_conversion_disambiguator> > constexpr year_month& > operator-=(const months& __dm) noexcept > { > @@ -2108,6 +2127,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > operator<=>(const year_month& __x, const year_month& __y) noexcept > = default; > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month > operator+(const year_month& __ym, const months& __dm) noexcept > { > @@ -2120,10 +2140,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return __y / __m; > } > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month > operator+(const months& __dm, const year_month& __ym) noexcept > { return __ym + __dm; } > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month > operator-(const year_month& __ym, const months& __dm) noexcept > { return __ym + -__dm; } > @@ -2200,6 +2222,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > : year_month_day(sys_days{__dp.time_since_epoch()}) > { } > > + template<typename = __detail::__months_years_conversion_disambiguator> > constexpr year_month_day& > operator+=(const months& __m) noexcept > { > @@ -2207,6 +2230,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return *this; > } > > + template<typename = __detail::__months_years_conversion_disambiguator> > constexpr year_month_day& > operator-=(const months& __m) noexcept > { > @@ -2262,10 +2286,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > operator<=>(const year_month_day& __x, const year_month_day& __y) > noexcept > = default; > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month_day > operator+(const year_month_day& __ymd, const months& __dm) noexcept > { return (__ymd.year() / __ymd.month() + __dm) / __ymd.day(); } > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month_day > operator+(const months& __dm, const year_month_day& __ymd) noexcept > { return __ymd + __dm; } > @@ -2278,6 +2304,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > operator+(const years& __dy, const year_month_day& __ymd) noexcept > { return __ymd + __dy; } > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month_day > operator-(const year_month_day& __ymd, const months& __dm) noexcept > { return __ymd + -__dm; } > @@ -2364,6 +2391,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > : _M_y{__y}, _M_mdl{__mdl} > { } > > + template<typename = __detail::__months_years_conversion_disambiguator> > constexpr year_month_day_last& > operator+=(const months& __m) noexcept > { > @@ -2371,6 +2399,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return *this; > } > > + template<typename = __detail::__months_years_conversion_disambiguator> > constexpr year_month_day_last& > operator-=(const months& __m) noexcept > { > @@ -2438,16 +2467,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > const year_month_day_last& __y) noexcept > = default; > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month_day_last > operator+(const year_month_day_last& __ymdl, > const months& __dm) noexcept > { return (__ymdl.year() / __ymdl.month() + __dm) / last; } > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month_day_last > operator+(const months& __dm, > const year_month_day_last& __ymdl) noexcept > { return __ymdl + __dm; } > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month_day_last > operator-(const year_month_day_last& __ymdl, > const months& __dm) noexcept > @@ -2544,6 +2576,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > : year_month_weekday{sys_days{__dp.time_since_epoch()}} > { } > > + template<typename = __detail::__months_years_conversion_disambiguator> > constexpr year_month_weekday& > operator+=(const months& __m) noexcept > { > @@ -2551,6 +2584,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return *this; > } > > + template<typename = __detail::__months_years_conversion_disambiguator> > constexpr year_month_weekday& > operator-=(const months& __m) noexcept > { > @@ -2626,10 +2660,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > && __x.weekday() == __y.weekday(); > } > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month_weekday > operator+(const year_month_weekday& __ymwd, const months& __dm) noexcept > - { return (__ymwd.year() / __ymwd.month() + __dm) / > __ymwd.weekday_indexed(); } > + { > + return ((__ymwd.year() / __ymwd.month() + __dm) > + / __ymwd.weekday_indexed()); > + } > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month_weekday > operator+(const months& __dm, const year_month_weekday& __ymwd) noexcept > { return __ymwd + __dm; } > @@ -2642,6 +2681,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > operator+(const years& __dy, const year_month_weekday& __ymwd) noexcept > { return __ymwd + __dy; } > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month_weekday > operator-(const year_month_weekday& __ymwd, const months& __dm) noexcept > { return __ymwd + -__dm; } > @@ -2690,6 +2730,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > : _M_y{__y}, _M_m{__m}, _M_wdl{__wdl} > { } > > + template<typename = __detail::__months_years_conversion_disambiguator> > constexpr year_month_weekday_last& > operator+=(const months& __m) noexcept > { > @@ -2697,6 +2738,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return *this; > } > > + template<typename = __detail::__months_years_conversion_disambiguator> > constexpr year_month_weekday_last& > operator-=(const months& __m) noexcept > { > @@ -2759,11 +2801,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > && __x.weekday_last() == __y.weekday_last(); > } > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month_weekday_last > operator+(const year_month_weekday_last& __ymwdl, > const months& __dm) noexcept > - { return (__ymwdl.year() / __ymwdl.month() + __dm) / > __ymwdl.weekday_last(); } > + { > + return ((__ymwdl.year() / __ymwdl.month() + __dm) > + / __ymwdl.weekday_last()); > + } > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month_weekday_last > operator+(const months& __dm, > const year_month_weekday_last& __ymwdl) noexcept > @@ -2779,6 +2826,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > const year_month_weekday_last& __ymwdl) noexcept > { return __ymwdl + __dy; } > > + template<typename = __detail::__months_years_conversion_disambiguator> > friend constexpr year_month_weekday_last > operator-(const year_month_weekday_last& __ymwdl, > const months& __dm) noexcept > diff --git a/libstdc++-v3/testsuite/std/time/year_month/2.cc > b/libstdc++-v3/testsuite/std/time/year_month/2.cc > new file mode 100644 > index 00000000000..36e14667547 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/time/year_month/2.cc > @@ -0,0 +1,40 @@ > +// { dg-options "-std=gnu++2a" } > +// { dg-do compile { target c++2a } } > + > +// Copyright (C) 2020 Free Software Foundation, Inc. > +// > +// This file is part of the GNU ISO C++ Library. This library is free > +// software; you can redistribute it and/or modify it under the > +// terms of the GNU General Public License as published by the > +// Free Software Foundation; either version 3, or (at your option) > +// any later version. > + > +// This library is distributed in the hope that it will be useful, > +// but WITHOUT ANY WARRANTY; without even the implied warranty of > +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +// GNU General Public License for more details. > + > +// You should have received a copy of the GNU General Public License along > +// with this library; see the file COPYING3. If not see > +// <http://www.gnu.org/licenses/>. > + > +// Class template year_month [time.cal.year_month] > + > +#include <chrono> > + > +constexpr void > +constexpr_year_month_op_overload_disambiguation() > +{ > + using namespace std::chrono; > + using decades = duration<long long, std::ratio<31556952 * 10>>; > + static_assert(std::convertible_to<decades, months> > + && std::convertible_to<decades, years>); > + using ym = year_month; > + > + constexpr ym ym1 = 2015y/June; > + static_assert(ym1 + decades{1} == 2025y/June); > + static_assert(ym1 - decades{1} == 2005y/June); > + static_assert(decades{1} + ym1 == 2025y/June); > + static_assert((ym{ym1} += decades{1}) == 2025y/June); > + static_assert((ym{ym1} -= decades{1}) == 2005y/June); > +} > diff --git a/libstdc++-v3/testsuite/std/time/year_month_day/2.cc > b/libstdc++-v3/testsuite/std/time/year_month_day/2.cc > new file mode 100644 > index 00000000000..80d1f033c1d > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/time/year_month_day/2.cc > @@ -0,0 +1,40 @@ > +// { dg-options "-std=gnu++2a" } > +// { dg-do compile { target c++2a } } > + > +// Copyright (C) 2020 Free Software Foundation, Inc. > +// > +// This file is part of the GNU ISO C++ Library. This library is free > +// software; you can redistribute it and/or modify it under the > +// terms of the GNU General Public License as published by the > +// Free Software Foundation; either version 3, or (at your option) > +// any later version. > + > +// This library is distributed in the hope that it will be useful, > +// but WITHOUT ANY WARRANTY; without even the implied warranty of > +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +// GNU General Public License for more details. > + > +// You should have received a copy of the GNU General Public License along > +// with this library; see the file COPYING3. If not see > +// <http://www.gnu.org/licenses/>. > + > +// Class template year_month_day [time.cal.year_month_day] > + > +#include <chrono> > + > +constexpr void > +constexpr_year_month_day_op_overload_disambiguation() > +{ > + using namespace std::chrono; > + using decades = duration<long long, std::ratio<31556952 * 10>>; > + static_assert(std::convertible_to<decades, months> > + && std::convertible_to<decades, years>); > + using ymd = year_month_day; > + > + constexpr ymd ymd1 = 2015y/June/15d; > + static_assert(ymd1 + decades{1} == 2025y/June/15d); > + static_assert(ymd1 - decades{1} == 2005y/June/15d); > + static_assert(decades{1} + ymd1 == 2025y/June/15d); > + static_assert((ymd{ymd1} += decades{1}) == 2025y/June/15d); > + static_assert((ymd{ymd1} -= decades{1}) == 2005y/June/15d); > +} > diff --git a/libstdc++-v3/testsuite/std/time/year_month_day_last/2.cc > b/libstdc++-v3/testsuite/std/time/year_month_day_last/2.cc > new file mode 100644 > index 00000000000..dadbd3c38b5 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/time/year_month_day_last/2.cc > @@ -0,0 +1,40 @@ > +// { dg-options "-std=gnu++2a" } > +// { dg-do compile { target c++2a } } > + > +// Copyright (C) 2020 Free Software Foundation, Inc. > +// > +// This file is part of the GNU ISO C++ Library. This library is free > +// software; you can redistribute it and/or modify it under the > +// terms of the GNU General Public License as published by the > +// Free Software Foundation; either version 3, or (at your option) > +// any later version. > + > +// This library is distributed in the hope that it will be useful, > +// but WITHOUT ANY WARRANTY; without even the implied warranty of > +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +// GNU General Public License for more details. > + > +// You should have received a copy of the GNU General Public License along > +// with this library; see the file COPYING3. If not see > +// <http://www.gnu.org/licenses/>. > + > +// Class template year_month_day_last [time.cal.year_month_day_last] > + > +#include <chrono> > + > +constexpr void > +constexpr_year_month_day_last_op_overload_disambiguation() > +{ > + using namespace std::chrono; > + using decades = duration<long long, std::ratio<31556952 * 10>>; > + static_assert(std::convertible_to<decades, months> > + && std::convertible_to<decades, years>); > + using ymdl = year_month_day_last; > + > + constexpr ymdl ymdl1 = 2015y/June/last; > + static_assert(ymdl1 + decades{1} == 2025y/June/last); > + static_assert(ymdl1 - decades{1} == 2005y/June/last); > + static_assert(decades{1} + ymdl1 == 2025y/June/last); > + static_assert((ymdl{ymdl1} += decades{1}) == 2025y/June/last); > + static_assert((ymdl{ymdl1} -= decades{1}) == 2005y/June/last); > +} > diff --git a/libstdc++-v3/testsuite/std/time/year_month_weekday/2.cc > b/libstdc++-v3/testsuite/std/time/year_month_weekday/2.cc > new file mode 100644 > index 00000000000..6ddfb15b283 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/time/year_month_weekday/2.cc > @@ -0,0 +1,40 @@ > +// { dg-options "-std=gnu++2a" } > +// { dg-do compile { target c++2a } } > + > +// Copyright (C) 2020 Free Software Foundation, Inc. > +// > +// This file is part of the GNU ISO C++ Library. This library is free > +// software; you can redistribute it and/or modify it under the > +// terms of the GNU General Public License as published by the > +// Free Software Foundation; either version 3, or (at your option) > +// any later version. > + > +// This library is distributed in the hope that it will be useful, > +// but WITHOUT ANY WARRANTY; without even the implied warranty of > +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +// GNU General Public License for more details. > + > +// You should have received a copy of the GNU General Public License along > +// with this library; see the file COPYING3. If not see > +// <http://www.gnu.org/licenses/>. > + > +// Class template year_month_weekday [time.cal.year_month_weekday] > + > +#include <chrono> > + > +constexpr void > +constexpr_year_month_weekday_op_overload_disambiguation() > +{ > + using namespace std::chrono; > + using decades = duration<long long, std::ratio<31556952 * 10>>; > + static_assert(std::convertible_to<decades, months> > + && std::convertible_to<decades, years>); > + using ymwd = year_month_weekday; > + > + constexpr ymwd ymwd1 = 2015y/June/Monday[3]; > + static_assert(ymwd1 + decades{1} == 2025y/June/Monday[3]); > + static_assert(ymwd1 - decades{1} == 2005y/June/Monday[3]); > + static_assert(decades{1} + ymwd1 == 2025y/June/Monday[3]); > + static_assert((ymwd{ymwd1} += decades{1}) == 2025y/June/Monday[3]); > + static_assert((ymwd{ymwd1} -= decades{1}) == 2005y/June/Monday[3]); > +} > diff --git a/libstdc++-v3/testsuite/std/time/year_month_weekday_last/2.cc > b/libstdc++-v3/testsuite/std/time/year_month_weekday_last/2.cc > new file mode 100644 > index 00000000000..170b5a45ad6 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/time/year_month_weekday_last/2.cc > @@ -0,0 +1,40 @@ > +// { dg-options "-std=gnu++2a" } > +// { dg-do compile { target c++2a } } > + > +// Copyright (C) 2020 Free Software Foundation, Inc. > +// > +// This file is part of the GNU ISO C++ Library. This library is free > +// software; you can redistribute it and/or modify it under the > +// terms of the GNU General Public License as published by the > +// Free Software Foundation; either version 3, or (at your option) > +// any later version. > + > +// This library is distributed in the hope that it will be useful, > +// but WITHOUT ANY WARRANTY; without even the implied warranty of > +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +// GNU General Public License for more details. > + > +// You should have received a copy of the GNU General Public License along > +// with this library; see the file COPYING3. If not see > +// <http://www.gnu.org/licenses/>. > + > +// Class template year_month_weekday_last [time.cal.year_month_weekday_last] > + > +#include <chrono> > + > +constexpr void > +constexpr_year_month_weekday_last_op_overload_disambiguation() > +{ > + using namespace std::chrono; > + using decades = duration<long long, std::ratio<31556952 * 10>>; > + static_assert(std::convertible_to<decades, months> > + && std::convertible_to<decades, years>); > + using ymwdl = year_month_weekday_last; > + > + constexpr ymwdl ymwdl1 = 2015y/June/Monday[last]; > + static_assert(ymwdl1 + decades{1} == 2025y/June/Monday[last]); > + static_assert(ymwdl1 - decades{1} == 2005y/June/Monday[last]); > + static_assert(decades{1} + ymwdl1 == 2025y/June/Monday[last]); > + static_assert((ymwdl{ymwdl1} += decades{1}) == 2025y/June/Monday[last]); > + static_assert((ymwdl{ymwdl1} -= decades{1}) == 2005y/June/Monday[last]); > +} > -- > 2.28.0.337.ge9b77c84a0 > >