[clang] [clang] [SemaCXX] Implement CWG2627 Bit-fields and narrowing conversions (PR #78112)
Mital Ashok via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 26 11:51:46 PDT 2024
https://github.com/MitalAshok updated https://github.com/llvm/llvm-project/pull/78112
>From 92f8720e3d21521b589d5291f086a2f32b87bfe0 Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Sun, 14 Jan 2024 19:52:31 +0000
Subject: [PATCH] [clang] [SemaCXX] Implement CWG2627 Bit-fields and narrowing
conversions
---
clang/docs/ReleaseNotes.rst | 5 +
.../clang/Basic/DiagnosticSemaKinds.td | 3 +
clang/include/clang/Sema/Overload.h | 7 +-
clang/lib/Sema/SemaExpr.cpp | 10 +-
clang/lib/Sema/SemaInit.cpp | 20 ++-
clang/lib/Sema/SemaOverload.cpp | 119 +++++++++------
.../dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp | 24 ++++
clang/test/CXX/drs/dr26xx.cpp | 136 ++++++++++++++++++
clang/www/cxx_dr_status.html | 2 +-
9 files changed, 278 insertions(+), 48 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 92563262cc6737..28202fc604e298 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -157,6 +157,11 @@ Resolutions to C++ Defect Reports
- Clang now diagnoses declarative nested-name-specifiers with pack-index-specifiers.
(`CWG2858: Declarative nested-name-specifiers and pack-index-specifiers <https://cplusplus.github.io/CWG/issues/2858.html>`_).
+- Casts from a bit-field to an integral type is now not considered narrowing if the
+ width of the bit-field means that all potential values are in the range
+ of the target type, even if the type of the bit-field is larger.
+ (`CWG2627. Bit-fields and narrowing conversions <https://cplusplus.github.io/CWG/issues/2627.html>`_).
+
C Language Changes
------------------
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index fdca82934cb4dc..6cdb439be30ae5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6253,6 +6253,9 @@ def ext_init_list_variable_narrowing_const_reference : ExtWarn<
def ext_init_list_constant_narrowing : ExtWarn<
"constant expression evaluates to %0 which cannot be narrowed to type %1">,
InGroup<CXX11Narrowing>, DefaultError, SFINAEFailure;
+def ext_bit_field_narrowing : Extension<
+ "narrowing non-constant-expression from %0 bit-field of width %2 to %1 is a C++23 extension">,
+ InGroup<CXX20CompatPedantic>, SFINAEFailure;
def ext_init_list_constant_narrowing_const_reference : ExtWarn<
ext_init_list_constant_narrowing.Summary>,
InGroup<CXX11NarrowingConstReference>, DefaultError, SFINAEFailure;
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 76311b00d2fc58..0d94045cc13f79 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -244,7 +244,11 @@ class Sema;
/// Not a narrowing conversion.
NK_Not_Narrowing,
- /// A narrowing conversion by virtue of the source and destination types.
+ /// Not a narrowing conversion in C++23 because the source is a bit-field
+ /// whose range can fit in the target type
+ NK_BitField_Not_Narrowing,
+
+ /// A narrowing conversion by virtue of the source and target types.
NK_Type_Narrowing,
/// A narrowing conversion, because a constant expression got narrowed.
@@ -387,6 +391,7 @@ class Sema;
NarrowingKind
getNarrowingKind(ASTContext &Context, const Expr *Converted,
APValue &ConstantValue, QualType &ConstantType,
+ unsigned &BitFieldWidth,
bool IgnoreFloatToIntegralConversion = false) const;
bool isPointerConversionToBool() const;
bool isPointerConversionToVoidPointer(ASTContext& Context) const;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 50f92c496a539a..4c16fcc60fc771 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -12361,8 +12361,9 @@ static bool checkThreeWayNarrowingConversion(Sema &S, QualType ToType, Expr *E,
APValue PreNarrowingValue;
QualType PreNarrowingType;
+ unsigned BitFieldWidth;
switch (SCS.getNarrowingKind(S.Context, E, PreNarrowingValue,
- PreNarrowingType,
+ PreNarrowingType, BitFieldWidth,
/*IgnoreFloatToIntegralConversion*/ true)) {
case NK_Dependent_Narrowing:
// Implicit conversion to a narrower type, but the expression is
@@ -12370,6 +12371,13 @@ static bool checkThreeWayNarrowingConversion(Sema &S, QualType ToType, Expr *E,
case NK_Not_Narrowing:
return false;
+ case NK_BitField_Not_Narrowing:
+ if (!S.getLangOpts().CPlusPlus23) {
+ return S.Diag(E->getBeginLoc(), diag::ext_bit_field_narrowing)
+ << FromType << ToType << BitFieldWidth;
+ }
+ return false;
+
case NK_Constant_Narrowing:
// Implicit conversion to a narrower type, and the value is not a constant
// expression.
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 003a157990d307..778fe53967bc54 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -10509,13 +10509,27 @@ static void DiagnoseNarrowingInInitList(Sema &S,
// C++11 [dcl.init.list]p7: Check whether this is a narrowing conversion.
APValue ConstantValue;
QualType ConstantType;
+ unsigned BitFieldWidth;
switch (SCS->getNarrowingKind(S.Context, PostInit, ConstantValue,
- ConstantType)) {
+ ConstantType, BitFieldWidth)) {
case NK_Not_Narrowing:
case NK_Dependent_Narrowing:
// No narrowing occurred.
return;
+ case NK_BitField_Not_Narrowing:
+ // A bit-field was "narrowed" to an integer type which is wider than the
+ // bit-field but the declared type of the bit-field is wider than the target
+ // type. This is not considered narrowing in C++23.
+ if (S.getLangOpts().CPlusPlus23)
+ return;
+ if (!(S.Diag(PostInit->getBeginLoc(), diag::ext_bit_field_narrowing)
+ << PreNarrowingType
+ << EntityType.getNonReferenceType().getLocalUnqualifiedType()
+ << BitFieldWidth))
+ return;
+ break;
+
case NK_Type_Narrowing: {
// This was a floating-to-integer conversion, which is always considered a
// narrowing conversion even if the value is a constant and can be
@@ -10592,9 +10606,10 @@ static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType,
APValue Value;
QualType PreNarrowingType;
+ unsigned BitFieldWidth;
// Reuse C++ narrowing check.
switch (ICS.Standard.getNarrowingKind(
- S.Context, Init, Value, PreNarrowingType,
+ S.Context, Init, Value, PreNarrowingType, BitFieldWidth,
/*IgnoreFloatToIntegralConversion*/ false)) {
// The value doesn't fit.
case NK_Constant_Narrowing:
@@ -10610,6 +10625,7 @@ static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType,
// Since we only reuse narrowing check for C23 constexpr variables here, we're
// not really interested in these cases.
+ case NK_BitField_Not_Narrowing:
case NK_Dependent_Narrowing:
case NK_Variable_Narrowing:
case NK_Not_Narrowing:
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 04cd9e78739d20..8bf42a4b1dfa4b 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -331,11 +331,14 @@ static const Expr *IgnoreNarrowingConversion(ASTContext &Ctx,
/// value of the expression prior to the narrowing conversion.
/// \param ConstantType If this is an NK_Constant_Narrowing conversion, the
/// type of the expression prior to the narrowing conversion.
+/// \param BitFieldWidth If this is an NK_BitField_Not_Narrowing conversion,
+/// the width of the source bit-field.
/// \param IgnoreFloatToIntegralConversion If true type-narrowing conversions
/// from floating point types to integral types should be ignored.
NarrowingKind StandardConversionSequence::getNarrowingKind(
ASTContext &Ctx, const Expr *Converted, APValue &ConstantValue,
- QualType &ConstantType, bool IgnoreFloatToIntegralConversion) const {
+ QualType &ConstantType, unsigned &BitFieldWidth,
+ bool IgnoreFloatToIntegralConversion) const {
assert((Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23) &&
"narrowing check outside C++");
@@ -463,7 +466,12 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
// -- from an integer type or unscoped enumeration type to an integer type
// that cannot represent all the values of the original type, except where
- // the source is a constant expression and the actual value after
+ // (C++23) -- the source is a bit-field whose width w is less than that of
+ // its type (or, for an enumeration type, its underlying type) and the
+ // target type can represent all the values of a hypothetical extended
+ // integer type with width w and with the same signedness as the original
+ // type or
+ // -- the source is a constant expression and the actual value after
// conversion will fit into the target type and will produce the original
// value when converted back to the original type.
case ICK_Integral_Conversion:
@@ -475,49 +483,70 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
const bool ToSigned = ToType->isSignedIntegerOrEnumerationType();
const unsigned ToWidth = Ctx.getIntWidth(ToType);
- if (FromWidth > ToWidth ||
- (FromWidth == ToWidth && FromSigned != ToSigned) ||
- (FromSigned && !ToSigned)) {
- // Not all values of FromType can be represented in ToType.
- const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted);
-
- // If it's value-dependent, we can't tell whether it's narrowing.
- if (Initializer->isValueDependent())
- return NK_Dependent_Narrowing;
+ constexpr auto CanRepresentAll = [](bool FromSigned, unsigned FromWidth,
+ bool ToSigned, unsigned ToWidth) {
+ return (FromWidth < ToWidth + (FromSigned == ToSigned)) &&
+ (FromSigned <= ToSigned);
+ };
- std::optional<llvm::APSInt> OptInitializerValue;
- if (!(OptInitializerValue = Initializer->getIntegerConstantExpr(Ctx))) {
- // Such conversions on variables are always narrowing.
- return NK_Variable_Narrowing;
- }
- llvm::APSInt &InitializerValue = *OptInitializerValue;
- bool Narrowing = false;
- if (FromWidth < ToWidth) {
- // Negative -> unsigned is narrowing. Otherwise, more bits is never
- // narrowing.
- if (InitializerValue.isSigned() && InitializerValue.isNegative())
- Narrowing = true;
- } else {
- // Add a bit to the InitializerValue so we don't have to worry about
- // signed vs. unsigned comparisons.
- InitializerValue = InitializerValue.extend(
- InitializerValue.getBitWidth() + 1);
- // Convert the initializer to and from the target width and signed-ness.
- llvm::APSInt ConvertedValue = InitializerValue;
- ConvertedValue = ConvertedValue.trunc(ToWidth);
- ConvertedValue.setIsSigned(ToSigned);
- ConvertedValue = ConvertedValue.extend(InitializerValue.getBitWidth());
- ConvertedValue.setIsSigned(InitializerValue.isSigned());
- // If the result is different, this was a narrowing conversion.
- if (ConvertedValue != InitializerValue)
- Narrowing = true;
- }
- if (Narrowing) {
- ConstantType = Initializer->getType();
- ConstantValue = APValue(InitializerValue);
- return NK_Constant_Narrowing;
+ if (CanRepresentAll(FromSigned, FromWidth, ToSigned, ToWidth))
+ return NK_Not_Narrowing;
+
+ // Not all values of FromType can be represented in ToType.
+ const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted);
+
+ // If it's value-dependent, we can't tell whether it's narrowing.
+ if (Initializer->isValueDependent())
+ return NK_Dependent_Narrowing;
+
+ std::optional<llvm::APSInt> OptInitializerValue;
+ if (!(OptInitializerValue = Initializer->getIntegerConstantExpr(Ctx))) {
+ // Check for bit-field whose width means that this would not be narrowing
+ // (This check is after checking for constant expressions because
+ // a constant expression that fits is never narrowing but a non-constant
+ // expression that comes from a bit-field is only not narrowing before
+ // C++23 as an extension)
+ if (const FieldDecl *BitField = Initializer->getSourceBitField()) {
+ if (BitField->getBitWidth()->isValueDependent()) {
+ return NK_Dependent_Narrowing;
+ }
+ BitFieldWidth = BitField->getBitWidthValue(Ctx);
+ if (CanRepresentAll(FromSigned, BitFieldWidth, ToSigned, ToWidth)) {
+ return NK_BitField_Not_Narrowing;
+ }
}
+
+ // Otherwise, such a conversion is always narrowing
+ return NK_Variable_Narrowing;
+ }
+ llvm::APSInt &InitializerValue = *OptInitializerValue;
+ bool Narrowing = false;
+ if (FromWidth < ToWidth) {
+ // Negative -> unsigned is narrowing. Otherwise, more bits is never
+ // narrowing.
+ if (InitializerValue.isSigned() && InitializerValue.isNegative())
+ Narrowing = true;
+ } else {
+ // Add a bit to the InitializerValue so we don't have to worry about
+ // signed vs. unsigned comparisons.
+ InitializerValue =
+ InitializerValue.extend(InitializerValue.getBitWidth() + 1);
+ // Convert the initializer to and from the target width and signed-ness.
+ llvm::APSInt ConvertedValue = InitializerValue;
+ ConvertedValue = ConvertedValue.trunc(ToWidth);
+ ConvertedValue.setIsSigned(ToSigned);
+ ConvertedValue = ConvertedValue.extend(InitializerValue.getBitWidth());
+ ConvertedValue.setIsSigned(InitializerValue.isSigned());
+ // If the result is different, this was a narrowing conversion.
+ if (ConvertedValue != InitializerValue)
+ Narrowing = true;
+ }
+ if (Narrowing) {
+ ConstantType = Initializer->getType();
+ ConstantValue = APValue(InitializerValue);
+ return NK_Constant_Narrowing;
}
+
return NK_Not_Narrowing;
}
case ICK_Complex_Real:
@@ -6232,14 +6261,18 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From,
// Check for a narrowing implicit conversion.
bool ReturnPreNarrowingValue = false;
QualType PreNarrowingType;
+ unsigned BitFieldWidth;
switch (SCS->getNarrowingKind(S.Context, Result.get(), PreNarrowingValue,
- PreNarrowingType)) {
+ PreNarrowingType, BitFieldWidth)) {
case NK_Dependent_Narrowing:
// Implicit conversion to a narrower type, but the expression is
// value-dependent so we can't tell whether it's actually narrowing.
case NK_Variable_Narrowing:
// Implicit conversion to a narrower type, and the value is not a constant
// expression. We'll diagnose this in a moment.
+ case NK_BitField_Not_Narrowing:
+ // Implicit conversion where the source is a bit-field and not a constant
+ // expression.
case NK_Not_Narrowing:
break;
diff --git a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp
index 2bceb3e267790d..a3049dc57d9fac 100644
--- a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp
@@ -203,6 +203,30 @@ void shrink_int() {
unsigned short usc1 = { c }; // expected-error {{non-constant-expression cannot be narrowed from type 'signed char'}} expected-note {{silence}}
unsigned short usc2 = { (signed char)'x' }; // OK
unsigned short usc3 = { (signed char)-1 }; // expected-error {{ -1 which cannot be narrowed}} expected-note {{silence}}
+
+#if __BITINT_MAXWIDTH__ >= 3
+ _BitInt(2) S2 = 0;
+ unsigned _BitInt(2) U2 = 0;
+ _BitInt(3) S3 = 0;
+ unsigned _BitInt(3) U3 = 0;
+
+ _BitInt(2) bi0 = { S2 };
+ _BitInt(2) bi1 = { U2 }; // expected-error {{cannot be narrowed from type 'unsigned _BitInt(2)'}}
+ _BitInt(2) bi2 = { S3 }; // expected-error {{cannot be narrowed from type '_BitInt(3)'}}
+ _BitInt(2) bi3 = { U3 }; // expected-error {{cannot be narrowed from type 'unsigned _BitInt(3)'}}
+ unsigned _BitInt(2) bi4 = { S2 }; // expected-error {{cannot be narrowed from type '_BitInt(2)'}}
+ unsigned _BitInt(2) bi5 = { U2 };
+ unsigned _BitInt(2) bi6 = { S3 }; // expected-error {{cannot be narrowed from type '_BitInt(3)'}}
+ unsigned _BitInt(2) bi7 = { U3 }; // expected-error {{cannot be narrowed from type 'unsigned _BitInt(3)'}}
+ _BitInt(3) bi8 = { S2 };
+ _BitInt(3) bi9 = { U2 };
+ _BitInt(3) bia = { S3 };
+ _BitInt(3) bib = { U3 }; // expected-error {{cannot be narrowed from type 'unsigned _BitInt(3)'}}
+ unsigned _BitInt(3) bic = { S2 }; // expected-error {{cannot be narrowed from type '_BitInt(2)'}}
+ unsigned _BitInt(3) bid = { U2 };
+ unsigned _BitInt(3) bie = { S3 }; // expected-error {{cannot be narrowed from type '_BitInt(3)'}}
+ unsigned _BitInt(3) bif = { U3 };
+#endif
}
// Be sure that type- and value-dependent expressions in templates get the error
diff --git a/clang/test/CXX/drs/dr26xx.cpp b/clang/test/CXX/drs/dr26xx.cpp
index f7a05b9827a235..3834325d69c4b5 100644
--- a/clang/test/CXX/drs/dr26xx.cpp
+++ b/clang/test/CXX/drs/dr26xx.cpp
@@ -1,11 +1,37 @@
// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,cxx11
+// RUN: %clang_cc1 -std=c++11 -pedantic -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,cxx11,until-cxx23-pedantic
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11
+// RUN: %clang_cc1 -std=c++14 -pedantic -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,until-cxx23-pedantic
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11
+// RUN: %clang_cc1 -std=c++17 -pedantic -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,until-cxx23-pedantic
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx20
+// RUN: %clang_cc1 -std=c++20 -pedantic -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx20,until-cxx23-pedantic
// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx20,since-cxx23
+// RUN: %clang_cc1 -std=c++23 -pedantic -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx20,since-cxx23
// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx20,since-cxx23
+// RUN: %clang_cc1 -std=c++2c -pedantic -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx20,since-cxx23
+namespace std {
+#if __cplusplus >= 202002L
+ struct strong_ordering {
+ int n;
+ constexpr operator int() const { return n; }
+ static const strong_ordering less, equal, greater;
+ };
+ constexpr strong_ordering strong_ordering::less{-1},
+ strong_ordering::equal{0}, strong_ordering::greater{1};
+#endif
+
+ typedef __INT16_TYPE__ int16_t;
+ typedef __UINT16_TYPE__ uint16_t;
+ typedef __INT32_TYPE__ int32_t;
+ typedef __UINT32_TYPE__ uint32_t;
+ typedef __INT64_TYPE__ int64_t;
+ typedef __UINT64_TYPE__ uint64_t;
+
+ template<typename T> T declval();
+}
namespace cwg2621 { // cwg2621: 16
#if __cplusplus >= 202002L
@@ -24,6 +50,116 @@ using enum E;
#endif
}
+namespace cwg2627 { // cwg2627: 19
+#if __cplusplus >= 202002L
+struct C {
+ long long i : 8;
+ friend auto operator<=>(C, C) = default; // #cwg2627-3way-def
+};
+
+void f() {
+ C x{1}, y{2};
+ static_cast<void>(x <=> y);
+// until-cxx23-pedantic-warning@#cwg2627-3way-def 2 {{narrowing non-constant-expression from 'long long' bit-field of width 8 to 'int' is a C++23 extension}}
+// until-cxx23-pedantic-note at -2 {{in defaulted three-way comparison operator for 'C' first required here}}
+ static_cast<void>(x.i <=> y.i);
+// until-cxx23-pedantic-warning at -1 2 {{narrowing non-constant-expression from 'long long' bit-field of width 8 to 'int' is a C++23 extension}}
+}
+
+template<typename T>
+struct CDependent {
+ T i : 8;
+ friend auto operator<=>(CDependent, CDependent) = default;
+};
+
+template<typename T>
+concept three_way_comparable = requires(T t) { { t <=> t }; };
+template<typename T>
+concept bf_three_way_comparable = requires(T t) { { t.i <=> t.i }; };
+static_assert(three_way_comparable<CDependent<long long>> == (__cplusplus >= 202302L));
+static_assert(bf_three_way_comparable<CDependent<long long>> == (__cplusplus >= 202302L));
+#endif
+
+#if __cplusplus >= 201103L
+template<int W>
+struct D {
+ __int128 i : W;
+};
+
+template<int W>
+std::int64_t f(D<W> d) {
+ return std::int64_t{ d.i }; // #cwg2627-f
+}
+
+template std::int64_t f(D<63>);
+// until-cxx23-pedantic-warning-re@#cwg2627-f {{narrowing non-constant-expression from '__int128' bit-field of width 63 to 'std::int64_t' (aka '{{.+}}') is a C++23 extension}}
+// until-cxx23-pedantic-note at -2 {{in instantiation of function template specialization 'cwg2627::f<63>' requested here}}
+// until-cxx23-pedantic-note@#cwg2627-f {{insert an explicit cast to silence this issue}}
+template std::int64_t f(D<64>);
+// until-cxx23-pedantic-warning-re@#cwg2627-f {{narrowing non-constant-expression from '__int128' bit-field of width 64 to 'std::int64_t' (aka '{{.+}}') is a C++23 extension}}
+// until-cxx23-pedantic-note at -2 {{in instantiation of function template specialization 'cwg2627::f<64>' requested here}}
+// until-cxx23-pedantic-note@#cwg2627-f {{insert an explicit cast to silence this issue}}
+template std::int64_t f(D<65>);
+// since-cxx11-error-re@#cwg2627-f {{non-constant-expression cannot be narrowed from type '__int128' to 'std::int64_t' (aka '{{.+}}') in initializer list}}
+// since-cxx11-note at -2 {{in instantiation of function template specialization 'cwg2627::f<65>' requested here}}
+// since-cxx11-note@#cwg2627-f {{insert an explicit cast to silence this issue}}
+
+template<typename Target, typename Source>
+Target g(Source x) {
+ return Target{ x.i }; // #cwg2627-g
+}
+
+template<typename T, int N>
+struct E {
+ T i : N;
+};
+
+template std::int16_t g(E<int, 16>);
+// until-cxx23-pedantic-warning-re@#cwg2627-g {{narrowing non-constant-expression from 'int' bit-field of width 16 to '{{.+}}' is a C++23 extension}}
+// until-cxx23-pedantic-note-re at -2 {{in instantiation of function template specialization 'cwg2627::g<{{.+}}, cwg2627::E<int, 16>>' requested here}}
+// until-cxx23-pedantic-note@#cwg2627-g {{insert an explicit cast to silence this issue}}
+template std::int16_t g(E<unsigned, 15>);
+// until-cxx23-pedantic-warning-re@#cwg2627-g {{narrowing non-constant-expression from 'unsigned int' bit-field of width 15 to '{{.+}}' is a C++23 extension}}
+// until-cxx23-pedantic-note-re at -2 {{in instantiation of function template specialization 'cwg2627::g<{{.+}}, cwg2627::E<unsigned int, 15>>' requested here}}
+// until-cxx23-pedantic-note@#cwg2627-g {{insert an explicit cast to silence this issue}}
+template std::int16_t g(E<unsigned, 16>);
+// since-cxx11-error-re@#cwg2627-g {{non-constant-expression cannot be narrowed from type 'unsigned int' to '{{.+}}' in initializer list}}
+// since-cxx11-note-re at -2 {{in instantiation of function template specialization 'cwg2627::g<{{.+}}, cwg2627::E<unsigned int, 16>>' requested here}}
+// since-cxx11-note@#cwg2627-g {{insert an explicit cast to silence this issue}}
+template std::uint16_t g(E<unsigned, 16>);
+// until-cxx23-pedantic-warning-re@#cwg2627-g {{narrowing non-constant-expression from 'unsigned int' bit-field of width 16 to '{{.+}}' is a C++23 extension}}
+// until-cxx23-pedantic-note-re at -2 {{in instantiation of function template specialization 'cwg2627::g<{{.+}}, cwg2627::E<unsigned int, 16>>' requested here}}
+// until-cxx23-pedantic-note@#cwg2627-g {{insert an explicit cast to silence this issue}}
+template std::uint16_t g(E<int, 1>);
+// since-cxx11-error-re@#cwg2627-g {{non-constant-expression cannot be narrowed from type 'int' to '{{.+}}' in initializer list}}
+// since-cxx11-note-re at -2 {{in instantiation of function template specialization 'cwg2627::g<{{.+}}, cwg2627::E<int, 1>>' requested here}}
+// since-cxx11-note@#cwg2627-g {{insert an explicit cast to silence this issue}}
+
+template bool g(E<unsigned, 1>);
+// until-cxx23-pedantic-warning@#cwg2627-g {{narrowing non-constant-expression from 'unsigned int' bit-field of width 1 to 'bool' is a C++23 extension}}
+// until-cxx23-pedantic-note at -2 {{in instantiation of function template specialization 'cwg2627::g<bool, cwg2627::E<unsigned int, 1>>' requested here}}
+// until-cxx23-pedantic-note@#cwg2627-g {{insert an explicit cast to silence this issue}}
+template bool g(E<int, 1>);
+// since-cxx11-error@#cwg2627-g {{non-constant-expression cannot be narrowed from type 'int' to 'bool' in initializer list}}
+// since-cxx11-note at -2 {{in instantiation of function template specialization 'cwg2627::g<bool, cwg2627::E<int, 1>>' requested here}}
+// since-cxx11-note@#cwg2627-g {{insert an explicit cast to silence this issue}}
+
+template<typename Target, typename Source>
+constexpr decltype(Target{ std::declval<Source>().i }, false) is_narrowing(int) { return false; }
+template<typename Target, typename Source>
+constexpr bool is_narrowing(long) { return true; }
+
+constexpr bool is_cxx23 = __cplusplus >= 202302L;
+static_assert(is_narrowing<std::int16_t, E<int, 16>>(0) == !is_cxx23, "");
+static_assert(is_narrowing<std::int16_t, E<unsigned, 15>>(0) == !is_cxx23, "");
+static_assert(is_narrowing<std::int16_t, E<unsigned, 16>>(0), "");
+static_assert(is_narrowing<std::uint16_t, E<unsigned, 16>>(0) == !is_cxx23, "");
+static_assert(is_narrowing<std::uint16_t, E<int, 1>>(0), "");
+static_assert(is_narrowing<bool, E<unsigned, 1>>(0) == !is_cxx23, "");
+static_assert(is_narrowing<bool, E<int, 1>>(0), "");
+#endif
+}
+
namespace cwg2628 { // cwg2628: no
// this was reverted for the 16.x release
// due to regressions, see the issue for more details:
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index ea8872c91be604..d1f28bad642796 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -15570,7 +15570,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/2627.html">2627</a></td>
<td>C++23</td>
<td>Bit-fields and narrowing conversions</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="unreleased" align="center">Clang 19</td>
</tr>
<tr id="2628">
<td><a href="https://cplusplus.github.io/CWG/issues/2628.html">2628</a></td>
More information about the cfe-commits
mailing list