From 7b743c67f04471a0129390ad2808e61e5538e0d3 Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Thu, 27 Aug 2020 14:09:52 -0400 Subject: [PATCH] libstdc++: Fix operator overload ambiguity with calendar types We currently don't enforce a constraint on some of the calendar types' addition/subtraction operator overloads that take a 'months' arguments: 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 where the given duration is convertible to both 'months' and to 'years' (as in the new testcases below). The correct behavior here in light of this constraint is to perform the operation through the (more efficient) 'years'-based overload, but we currently emit an ambiguous overload error. This patch templatizes the 'months'-based addition/subtraction operator overloads so that in the event of an implicit-conversion tie, we select the non-template 'years'-based overload. This is the same approach that the date library takes for enforcing this constraint. libstdc++-v3/ChangeLog: * include/std/chrono (__detail::__months_years_conversion_disambiguator): Define. (year_month::operator+=): Templatize the 'months'-based overload so that the 'years'-based overload is selected in case of equally-ranked implicit conversion sequences 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 | 286 ++++++++++-------- .../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, 367 insertions(+), 119 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 01f1e2d2b71..df2f5d23f52 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'-based + // 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 templatizing the 'months'-based + // overloads (using a dummy defaulted template parameter), so that + // overload resolution doesn't select the 'months'-based 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,19 +2085,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION month() const noexcept { return _M_m; } - constexpr year_month& - operator+=(const months& __dm) noexcept - { - *this = *this + __dm; - return *this; - } + template + constexpr year_month& + operator+=(const months& __dm) noexcept + { + *this = *this + __dm; + return *this; + } - constexpr year_month& - operator-=(const months& __dm) noexcept - { - *this = *this - __dm; - return *this; - } + template + constexpr year_month& + operator-=(const months& __dm) noexcept + { + *this = *this - __dm; + return *this; + } constexpr year_month& operator+=(const years& __dy) noexcept @@ -2108,25 +2127,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION operator<=>(const year_month& __x, const year_month& __y) noexcept = default; - friend constexpr year_month - operator+(const year_month& __ym, const months& __dm) noexcept - { - // TODO: Optimize? - auto __m = __ym.month() + __dm; - auto __i = unsigned{__ym.month()} - 1 + __dm.count(); - auto __y = (__i < 0 - ? __ym.year() + years{(__i - 11) / 12} - : __ym.year() + years{__i / 12}); - return __y / __m; - } + template + friend constexpr year_month + operator+(const year_month& __ym, const months& __dm) noexcept + { + // TODO: Optimize? + auto __m = __ym.month() + __dm; + auto __i = unsigned{__ym.month()} - 1 + __dm.count(); + auto __y = (__i < 0 + ? __ym.year() + years{(__i - 11) / 12} + : __ym.year() + years{__i / 12}); + return __y / __m; + } - friend constexpr year_month - operator+(const months& __dm, const year_month& __ym) noexcept - { return __ym + __dm; } + template + friend constexpr year_month + operator+(const months& __dm, const year_month& __ym) noexcept + { return __ym + __dm; } - friend constexpr year_month - operator-(const year_month& __ym, const months& __dm) noexcept - { return __ym + -__dm; } + template + friend constexpr year_month + operator-(const year_month& __ym, const months& __dm) noexcept + { return __ym + -__dm; } friend constexpr months operator-(const year_month& __x, const year_month& __y) noexcept @@ -2200,19 +2222,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : year_month_day(sys_days{__dp.time_since_epoch()}) { } - constexpr year_month_day& - operator+=(const months& __m) noexcept - { - *this = *this + __m; - return *this; - } + template + constexpr year_month_day& + operator+=(const months& __m) noexcept + { + *this = *this + __m; + return *this; + } - constexpr year_month_day& - operator-=(const months& __m) noexcept - { - *this = *this - __m; - return *this; - } + template + constexpr year_month_day& + operator-=(const months& __m) noexcept + { + *this = *this - __m; + return *this; + } constexpr year_month_day& operator+=(const years& __y) noexcept @@ -2262,13 +2286,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION operator<=>(const year_month_day& __x, const year_month_day& __y) noexcept = default; - friend constexpr year_month_day - operator+(const year_month_day& __ymd, const months& __dm) noexcept - { return (__ymd.year() / __ymd.month() + __dm) / __ymd.day(); } + template + friend constexpr year_month_day + operator+(const year_month_day& __ymd, const months& __dm) noexcept + { return (__ymd.year() / __ymd.month() + __dm) / __ymd.day(); } - friend constexpr year_month_day - operator+(const months& __dm, const year_month_day& __ymd) noexcept - { return __ymd + __dm; } + template + friend constexpr year_month_day + operator+(const months& __dm, const year_month_day& __ymd) noexcept + { return __ymd + __dm; } friend constexpr year_month_day operator+(const year_month_day& __ymd, const years& __dy) noexcept @@ -2278,9 +2304,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION operator+(const years& __dy, const year_month_day& __ymd) noexcept { return __ymd + __dy; } - friend constexpr year_month_day - operator-(const year_month_day& __ymd, const months& __dm) noexcept - { return __ymd + -__dm; } + template + friend constexpr year_month_day + operator-(const year_month_day& __ymd, const months& __dm) noexcept + { return __ymd + -__dm; } friend constexpr year_month_day operator-(const year_month_day& __ymd, const years& __dy) noexcept @@ -2364,19 +2391,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _M_y{__y}, _M_mdl{__mdl} { } - constexpr year_month_day_last& - operator+=(const months& __m) noexcept - { - *this = *this + __m; - return *this; - } + template + constexpr year_month_day_last& + operator+=(const months& __m) noexcept + { + *this = *this + __m; + return *this; + } - constexpr year_month_day_last& - operator-=(const months& __m) noexcept - { - *this = *this - __m; - return *this; - } + template + constexpr year_month_day_last& + operator-=(const months& __m) noexcept + { + *this = *this - __m; + return *this; + } constexpr year_month_day_last& operator+=(const years& __y) noexcept @@ -2438,20 +2467,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const year_month_day_last& __y) noexcept = default; - 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 + friend constexpr year_month_day_last + operator+(const year_month_day_last& __ymdl, + const months& __dm) noexcept + { return (__ymdl.year() / __ymdl.month() + __dm) / last; } - friend constexpr year_month_day_last - operator+(const months& __dm, - const year_month_day_last& __ymdl) noexcept - { return __ymdl + __dm; } + template + friend constexpr year_month_day_last + operator+(const months& __dm, + const year_month_day_last& __ymdl) noexcept + { return __ymdl + __dm; } - friend constexpr year_month_day_last - operator-(const year_month_day_last& __ymdl, - const months& __dm) noexcept - { return __ymdl + -__dm; } + template + friend constexpr year_month_day_last + operator-(const year_month_day_last& __ymdl, + const months& __dm) noexcept + { return __ymdl + -__dm; } friend constexpr year_month_day_last operator+(const year_month_day_last& __ymdl, @@ -2544,19 +2576,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : year_month_weekday{sys_days{__dp.time_since_epoch()}} { } - constexpr year_month_weekday& - operator+=(const months& __m) noexcept - { - *this = *this + __m; - return *this; - } + template + constexpr year_month_weekday& + operator+=(const months& __m) noexcept + { + *this = *this + __m; + return *this; + } - constexpr year_month_weekday& - operator-=(const months& __m) noexcept - { - *this = *this - __m; - return *this; - } + template + constexpr year_month_weekday& + operator-=(const months& __m) noexcept + { + *this = *this - __m; + return *this; + } constexpr year_month_weekday& operator+=(const years& __y) noexcept @@ -2626,13 +2660,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION && __x.weekday_indexed() == __y.weekday_indexed(); } - friend constexpr year_month_weekday - operator+(const year_month_weekday& __ymwd, const months& __dm) noexcept - { return (__ymwd.year() / __ymwd.month() + __dm) / __ymwd.weekday_indexed(); } + template + friend constexpr year_month_weekday + operator+(const year_month_weekday& __ymwd, const months& __dm) noexcept + { + return ((__ymwd.year() / __ymwd.month() + __dm) + / __ymwd.weekday_indexed()); + } - friend constexpr year_month_weekday - operator+(const months& __dm, const year_month_weekday& __ymwd) noexcept - { return __ymwd + __dm; } + template + friend constexpr year_month_weekday + operator+(const months& __dm, const year_month_weekday& __ymwd) noexcept + { return __ymwd + __dm; } friend constexpr year_month_weekday operator+(const year_month_weekday& __ymwd, const years& __dy) noexcept @@ -2642,9 +2681,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION operator+(const years& __dy, const year_month_weekday& __ymwd) noexcept { return __ymwd + __dy; } - friend constexpr year_month_weekday - operator-(const year_month_weekday& __ymwd, const months& __dm) noexcept - { return __ymwd + -__dm; } + template + friend constexpr year_month_weekday + operator-(const year_month_weekday& __ymwd, const months& __dm) noexcept + { return __ymwd + -__dm; } friend constexpr year_month_weekday operator-(const year_month_weekday& __ymwd, const years& __dy) noexcept @@ -2690,19 +2730,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _M_y{__y}, _M_m{__m}, _M_wdl{__wdl} { } - constexpr year_month_weekday_last& - operator+=(const months& __m) noexcept - { - *this = *this + __m; - return *this; - } + template + constexpr year_month_weekday_last& + operator+=(const months& __m) noexcept + { + *this = *this + __m; + return *this; + } - constexpr year_month_weekday_last& - operator-=(const months& __m) noexcept - { - *this = *this - __m; - return *this; - } + template + constexpr year_month_weekday_last& + operator-=(const months& __m) noexcept + { + *this = *this - __m; + return *this; + } constexpr year_month_weekday_last& operator+=(const years& __y) noexcept @@ -2759,15 +2801,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION && __x.weekday_last() == __y.weekday_last(); } - 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(); } + template + 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()); + } - friend constexpr year_month_weekday_last - operator+(const months& __dm, - const year_month_weekday_last& __ymwdl) noexcept - { return __ymwdl + __dm; } + template + friend constexpr year_month_weekday_last + operator+(const months& __dm, + const year_month_weekday_last& __ymwdl) noexcept + { return __ymwdl + __dm; } friend constexpr year_month_weekday_last operator+(const year_month_weekday_last& __ymwdl, @@ -2779,10 +2826,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const year_month_weekday_last& __ymwdl) noexcept { return __ymwdl + __dy; } - friend constexpr year_month_weekday_last - operator-(const year_month_weekday_last& __ymwdl, - const months& __dm) noexcept - { return __ymwdl + -__dm; } + template + friend constexpr year_month_weekday_last + operator-(const year_month_weekday_last& __ymwdl, + const months& __dm) noexcept + { return __ymwdl + -__dm; } friend constexpr year_month_weekday_last operator-(const year_month_weekday_last& __ymwdl, 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 +// . + +// Class template year_month [time.cal.year_month] + +#include + +constexpr void +constexpr_year_month_op_overload_disambiguation() +{ + using namespace std::chrono; + using decades = duration>; + static_assert(std::convertible_to + && std::convertible_to); + 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 +// . + +// Class template year_month_day [time.cal.year_month_day] + +#include + +constexpr void +constexpr_year_month_day_op_overload_disambiguation() +{ + using namespace std::chrono; + using decades = duration>; + static_assert(std::convertible_to + && std::convertible_to); + 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 +// . + +// Class template year_month_day_last [time.cal.year_month_day_last] + +#include + +constexpr void +constexpr_year_month_day_last_op_overload_disambiguation() +{ + using namespace std::chrono; + using decades = duration>; + static_assert(std::convertible_to + && std::convertible_to); + 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 +// . + +// Class template year_month_weekday [time.cal.year_month_weekday] + +#include + +constexpr void +constexpr_year_month_weekday_op_overload_disambiguation() +{ + using namespace std::chrono; + using decades = duration>; + static_assert(std::convertible_to + && std::convertible_to); + 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 +// . + +// Class template year_month_weekday_last [time.cal.year_month_weekday_last] + +#include + +constexpr void +constexpr_year_month_weekday_last_op_overload_disambiguation() +{ + using namespace std::chrono; + using decades = duration>; + static_assert(std::convertible_to + && std::convertible_to); + 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.30.2