Skip to content

Commit 574e958

Browse files
authored
[clang] Implement CWG2627 Bit-fields and narrowing conversions (#78112)
https://cplusplus.github.io/CWG/issues/2627.html It is no longer a narrowing conversion when converting a bit-field to a type smaller than the field's declared type if the bit-field has a width small enough to fit in the target type. This includes integral promotions (`long long i : 8` promoted to `int` is no longer narrowing, allowing `c.i <=> c.i`) and list-initialization (`int n{ c.i };`) Also applies back to C++11 as this is a defect report.
1 parent 3e806c8 commit 574e958

File tree

6 files changed

+238
-49
lines changed

6 files changed

+238
-49
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ Resolutions to C++ Defect Reports
111111
Otherwise, if there is no initializer list constructor, the copy will be elided as if it was ``T(e)``.
112112
(`CWG2311: Missed case for guaranteed copy elision <https://cplusplus.github.io/CWG/issues/2311.html>`)
113113

114+
- Casts from a bit-field to an integral type is now not considered narrowing if the
115+
width of the bit-field means that all potential values are in the range
116+
of the target type, even if the type of the bit-field is larger.
117+
(`CWG2627: Bit-fields and narrowing conversions <https://cplusplus.github.io/CWG/issues/2627.html>`_)
118+
114119
C Language Changes
115120
------------------
116121

clang/lib/Sema/SemaOverload.cpp

Lines changed: 73 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -489,61 +489,93 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
489489

490490
// -- from an integer type or unscoped enumeration type to an integer type
491491
// that cannot represent all the values of the original type, except where
492-
// the source is a constant expression and the actual value after
492+
// (CWG2627) -- the source is a bit-field whose width w is less than that
493+
// of its type (or, for an enumeration type, its underlying type) and the
494+
// target type can represent all the values of a hypothetical extended
495+
// integer type with width w and with the same signedness as the original
496+
// type or
497+
// -- the source is a constant expression and the actual value after
493498
// conversion will fit into the target type and will produce the original
494499
// value when converted back to the original type.
495500
case ICK_Integral_Conversion:
496501
IntegralConversion: {
497502
assert(FromType->isIntegralOrUnscopedEnumerationType());
498503
assert(ToType->isIntegralOrUnscopedEnumerationType());
499504
const bool FromSigned = FromType->isSignedIntegerOrEnumerationType();
500-
const unsigned FromWidth = Ctx.getIntWidth(FromType);
505+
unsigned FromWidth = Ctx.getIntWidth(FromType);
501506
const bool ToSigned = ToType->isSignedIntegerOrEnumerationType();
502507
const unsigned ToWidth = Ctx.getIntWidth(ToType);
503508

504-
if (FromWidth > ToWidth ||
505-
(FromWidth == ToWidth && FromSigned != ToSigned) ||
506-
(FromSigned && !ToSigned)) {
507-
// Not all values of FromType can be represented in ToType.
508-
const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted);
509+
constexpr auto CanRepresentAll = [](bool FromSigned, unsigned FromWidth,
510+
bool ToSigned, unsigned ToWidth) {
511+
return (FromWidth < ToWidth + (FromSigned == ToSigned)) &&
512+
(FromSigned <= ToSigned);
513+
};
509514

510-
// If it's value-dependent, we can't tell whether it's narrowing.
511-
if (Initializer->isValueDependent())
512-
return NK_Dependent_Narrowing;
515+
if (CanRepresentAll(FromSigned, FromWidth, ToSigned, ToWidth))
516+
return NK_Not_Narrowing;
513517

514-
std::optional<llvm::APSInt> OptInitializerValue;
515-
if (!(OptInitializerValue = Initializer->getIntegerConstantExpr(Ctx))) {
516-
// Such conversions on variables are always narrowing.
517-
return NK_Variable_Narrowing;
518-
}
519-
llvm::APSInt &InitializerValue = *OptInitializerValue;
520-
bool Narrowing = false;
521-
if (FromWidth < ToWidth) {
522-
// Negative -> unsigned is narrowing. Otherwise, more bits is never
523-
// narrowing.
524-
if (InitializerValue.isSigned() && InitializerValue.isNegative())
525-
Narrowing = true;
526-
} else {
527-
// Add a bit to the InitializerValue so we don't have to worry about
528-
// signed vs. unsigned comparisons.
529-
InitializerValue = InitializerValue.extend(
530-
InitializerValue.getBitWidth() + 1);
531-
// Convert the initializer to and from the target width and signed-ness.
532-
llvm::APSInt ConvertedValue = InitializerValue;
533-
ConvertedValue = ConvertedValue.trunc(ToWidth);
534-
ConvertedValue.setIsSigned(ToSigned);
535-
ConvertedValue = ConvertedValue.extend(InitializerValue.getBitWidth());
536-
ConvertedValue.setIsSigned(InitializerValue.isSigned());
537-
// If the result is different, this was a narrowing conversion.
538-
if (ConvertedValue != InitializerValue)
539-
Narrowing = true;
540-
}
541-
if (Narrowing) {
542-
ConstantType = Initializer->getType();
543-
ConstantValue = APValue(InitializerValue);
544-
return NK_Constant_Narrowing;
518+
// Not all values of FromType can be represented in ToType.
519+
const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted);
520+
521+
bool DependentBitField = false;
522+
if (const FieldDecl *BitField = Initializer->getSourceBitField()) {
523+
if (BitField->getBitWidth()->isValueDependent())
524+
DependentBitField = true;
525+
else if (unsigned BitFieldWidth = BitField->getBitWidthValue(Ctx);
526+
BitFieldWidth < FromWidth) {
527+
if (CanRepresentAll(FromSigned, BitFieldWidth, ToSigned, ToWidth))
528+
return NK_Not_Narrowing;
529+
530+
// The initializer will be truncated to the bit-field width
531+
FromWidth = BitFieldWidth;
545532
}
546533
}
534+
535+
// If it's value-dependent, we can't tell whether it's narrowing.
536+
if (Initializer->isValueDependent())
537+
return NK_Dependent_Narrowing;
538+
539+
std::optional<llvm::APSInt> OptInitializerValue =
540+
Initializer->getIntegerConstantExpr(Ctx);
541+
if (!OptInitializerValue) {
542+
// If the bit-field width was dependent, it might end up being small
543+
// enough to fit in the target type (unless the target type is unsigned
544+
// and the source type is signed, in which case it will never fit)
545+
if (DependentBitField && (FromSigned <= ToSigned))
546+
return NK_Dependent_Narrowing;
547+
548+
// Otherwise, such a conversion is always narrowing
549+
return NK_Variable_Narrowing;
550+
}
551+
llvm::APSInt &InitializerValue = *OptInitializerValue;
552+
bool Narrowing = false;
553+
if (FromWidth < ToWidth) {
554+
// Negative -> unsigned is narrowing. Otherwise, more bits is never
555+
// narrowing.
556+
if (InitializerValue.isSigned() && InitializerValue.isNegative())
557+
Narrowing = true;
558+
} else {
559+
// Add a bit to the InitializerValue so we don't have to worry about
560+
// signed vs. unsigned comparisons.
561+
InitializerValue =
562+
InitializerValue.extend(InitializerValue.getBitWidth() + 1);
563+
// Convert the initializer to and from the target width and signed-ness.
564+
llvm::APSInt ConvertedValue = InitializerValue;
565+
ConvertedValue = ConvertedValue.trunc(ToWidth);
566+
ConvertedValue.setIsSigned(ToSigned);
567+
ConvertedValue = ConvertedValue.extend(InitializerValue.getBitWidth());
568+
ConvertedValue.setIsSigned(InitializerValue.isSigned());
569+
// If the result is different, this was a narrowing conversion.
570+
if (ConvertedValue != InitializerValue)
571+
Narrowing = true;
572+
}
573+
if (Narrowing) {
574+
ConstantType = Initializer->getType();
575+
ConstantValue = APValue(InitializerValue);
576+
return NK_Constant_Narrowing;
577+
}
578+
547579
return NK_Not_Narrowing;
548580
}
549581
case ICK_Complex_Real:

clang/test/CXX/drs/cwg26xx.cpp

Lines changed: 116 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,39 @@
1-
// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown -pedantic-errors %s -verify=expected
2-
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown -pedantic-errors %s -verify=expected,since-cxx11,cxx11
3-
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown -pedantic-errors %s -verify=expected,since-cxx11
4-
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown -pedantic-errors %s -verify=expected,since-cxx11
5-
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown -pedantic-errors %s -verify=expected,since-cxx11,since-cxx20
6-
// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown -pedantic-errors %s -verify=expected,since-cxx11,since-cxx20,since-cxx23
7-
// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown -pedantic-errors %s -verify=expected,since-cxx11,since-cxx20,since-cxx23
1+
// RUN: %clang_cc1 -std=c++98 -pedantic-errors %s -verify=expected,cxx98
2+
// RUN: %clang_cc1 -std=c++11 -pedantic-errors %s -verify=expected,since-cxx11,cxx11
3+
// RUN: %clang_cc1 -std=c++14 -pedantic-errors %s -verify=expected,since-cxx11
4+
// RUN: %clang_cc1 -std=c++17 -pedantic-errors %s -verify=expected,since-cxx11
5+
// RUN: %clang_cc1 -std=c++20 -pedantic-errors %s -verify=expected,since-cxx11,since-cxx20
6+
// RUN: %clang_cc1 -std=c++23 -pedantic-errors %s -verify=expected,since-cxx11,since-cxx20,since-cxx23
7+
// RUN: %clang_cc1 -std=c++2c -pedantic-errors %s -verify=expected,since-cxx11,since-cxx20,since-cxx23
8+
9+
#if __cplusplus == 199711L
10+
#define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
11+
// cxx98-error@-1 {{variadic macros are a C99 feature}}
12+
#endif
813

14+
namespace std {
15+
#if __cplusplus >= 202002L
16+
struct strong_ordering {
17+
int n;
18+
constexpr operator int() const { return n; }
19+
static const strong_ordering less, equal, greater;
20+
};
21+
constexpr strong_ordering strong_ordering::less{-1},
22+
strong_ordering::equal{0}, strong_ordering::greater{1};
23+
#endif
24+
25+
typedef short int16_t;
26+
typedef unsigned short uint16_t;
27+
typedef int int32_t;
28+
typedef unsigned uint32_t;
29+
typedef long long int64_t;
30+
// cxx98-error@-1 {{'long long' is a C++11 extension}}
31+
typedef unsigned long long uint64_t;
32+
// cxx98-error@-1 {{'long long' is a C++11 extension}}
33+
static_assert(sizeof(int16_t) == 2 && sizeof(int32_t) == 4 && sizeof(int64_t) == 8, "Some tests rely on these sizes");
34+
35+
template<typename T> T declval();
36+
}
937

1038
namespace cwg2621 { // cwg2621: sup 2877
1139
#if __cplusplus >= 202002L
@@ -23,6 +51,87 @@ using enum E;
2351
#endif
2452
}
2553

54+
namespace cwg2627 { // cwg2627: 20
55+
#if __cplusplus >= 202002L
56+
struct C {
57+
long long i : 8;
58+
friend auto operator<=>(C, C) = default;
59+
};
60+
61+
void f() {
62+
C x{1}, y{2};
63+
static_cast<void>(x <=> y);
64+
static_cast<void>(x.i <=> y.i);
65+
}
66+
67+
template<typename T>
68+
struct CDependent {
69+
T i : 8;
70+
friend auto operator<=>(CDependent, CDependent) = default;
71+
};
72+
73+
template<typename T>
74+
concept three_way_comparable = requires(T t) { { t <=> t }; };
75+
template<typename T>
76+
concept bf_three_way_comparable = requires(T t) { { t.i <=> t.i }; };
77+
static_assert(three_way_comparable<CDependent<long long>>);
78+
static_assert(bf_three_way_comparable<CDependent<long long>>);
79+
#endif
80+
81+
#if __cplusplus >= 201103L
82+
template<typename T, int N>
83+
struct D {
84+
T i : N;
85+
};
86+
87+
template<typename T, int N>
88+
D<T, N> d();
89+
90+
std::int32_t d1{ d<std::int64_t, 31>().i };
91+
std::int32_t d2{ d<std::int64_t, 32>().i };
92+
std::int32_t d3{ d<std::int64_t, 33>().i };
93+
// since-cxx11-error@-1 {{non-constant-expression cannot be narrowed from type 'long long' to 'std::int32_t' (aka 'int') in initializer list}}
94+
// since-cxx11-note@-2 {{insert an explicit cast to silence this issue}}
95+
96+
std::int16_t d6{ d<int, 16>().i };
97+
std::int16_t d7{ d<unsigned, 15>().i };
98+
std::int16_t d8{ d<unsigned, 16>().i };
99+
// since-cxx11-error@-1 {{non-constant-expression cannot be narrowed from type 'unsigned int' to 'std::int16_t' (aka 'short') in initializer list}}
100+
// since-cxx11-note@-2 {{insert an explicit cast to silence this issue}}
101+
std::uint16_t d9{ d<unsigned, 16>().i };
102+
std::uint16_t da{ d<int, 1>().i };
103+
// since-cxx11-error@-1 {{non-constant-expression cannot be narrowed from type 'int' to 'std::uint16_t' (aka 'unsigned short') in initializer list}}
104+
// since-cxx11-note@-2 {{insert an explicit cast to silence this issue}}
105+
106+
bool db{ d<unsigned, 1>().i };
107+
bool dc{ d<int, 1>().i };
108+
// since-cxx11-error@-1 {{non-constant-expression cannot be narrowed from type 'int' to 'bool' in initializer list}}
109+
// since-cxx11-note@-2 {{insert an explicit cast to silence this issue}}
110+
111+
template<typename Target, typename Source>
112+
constexpr decltype(Target{ std::declval<Source>().i }, false) is_narrowing(int) { return false; }
113+
template<typename Target, typename Source>
114+
constexpr bool is_narrowing(long) { return true; }
115+
116+
static_assert(!is_narrowing<std::int16_t, D<int, 16>>(0), "");
117+
static_assert(!is_narrowing<std::int16_t, D<unsigned, 15>>(0), "");
118+
static_assert(is_narrowing<std::int16_t, D<unsigned, 16>>(0), "");
119+
static_assert(!is_narrowing<std::uint16_t, D<unsigned, 16>>(0), "");
120+
static_assert(is_narrowing<std::uint16_t, D<int, 1>>(0), "");
121+
static_assert(!is_narrowing<bool, D<unsigned, 1>>(0), "");
122+
static_assert(is_narrowing<bool, D<int, 1>>(0), "");
123+
124+
template<int N>
125+
struct E {
126+
signed int x : N;
127+
decltype(std::int16_t{ x }) dependent_narrowing;
128+
decltype(unsigned{ x }) always_narrowing;
129+
// since-cxx11-error@-1 {{non-constant-expression cannot be narrowed from type 'int' to 'unsigned int' in initializer list}}
130+
// since-cxx11-note@-2 {{insert an explicit cast to silence this issue}}
131+
};
132+
#endif
133+
} // namespace cwg2627
134+
26135
namespace cwg2628 { // cwg2628: no
27136
// this was reverted for the 16.x release
28137
// due to regressions, see the issue for more details:

clang/test/Sema/constexpr.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,3 +360,10 @@ void infsNaNs() {
360360

361361
constexpr struct S9 s9 = { }; // expected-error {{variable has incomplete type 'const struct S9'}} \
362362
// expected-note {{forward declaration of 'struct S9'}}
363+
364+
struct S10 {
365+
signed long long i : 8;
366+
};
367+
constexpr struct S10 c = { 255 };
368+
// FIXME-expected-error@-1 {{constexpr initializer evaluates to 255 which is not exactly representable in 'long long' bit-field with width 8}}
369+
// See: GH#101299
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
2+
// RUN: %clang_cc1 -triple x86_64 -fsyntax-only -verify -std=c++11 %s
3+
// RUN: %clang_cc1 -triple i386 -fsyntax-only -verify -std=c++11 %s
4+
5+
struct {
6+
_BitInt(35) i : 33;
7+
} x;
8+
struct {
9+
_BitInt(35) i : 34;
10+
} y;
11+
_BitInt(33) xx{ x.i };
12+
_BitInt(33) yy{ y.i };
13+
// expected-error@-1 {{non-constant-expression cannot be narrowed from type '_BitInt(35)' to '_BitInt(33)' in initializer list}}
14+
// FIXME-expected-note@-2 {{insert an explicit cast to silence this issue}}
15+
16+
_BitInt(2) S2 = 0;
17+
unsigned _BitInt(2) U2 = 0;
18+
_BitInt(3) S3 = 0;
19+
unsigned _BitInt(3) U3 = 0;
20+
21+
_BitInt(2) bi0{ S2 };
22+
_BitInt(2) bi1{ U2 }; // expected-error {{non-constant-expression cannot be narrowed from type 'unsigned _BitInt(2)' to '_BitInt(2)' in initializer list}}
23+
_BitInt(2) bi2{ S3 }; // expected-error {{non-constant-expression cannot be narrowed from type '_BitInt(3)' to '_BitInt(2)' in initializer list}}
24+
_BitInt(2) bi3{ U3 }; // expected-error {{non-constant-expression cannot be narrowed from type 'unsigned _BitInt(3)' to '_BitInt(2)' in initializer list}}
25+
unsigned _BitInt(2) bi4{ S2 }; // expected-error {{non-constant-expression cannot be narrowed from type '_BitInt(2)' to 'unsigned _BitInt(2)' in initializer list}}
26+
unsigned _BitInt(2) bi5{ U2 };
27+
unsigned _BitInt(2) bi6{ S3 }; // expected-error {{non-constant-expression cannot be narrowed from type '_BitInt(3)' to 'unsigned _BitInt(2)' in initializer list}}
28+
unsigned _BitInt(2) bi7{ U3 }; // expected-error {{non-constant-expression cannot be narrowed from type 'unsigned _BitInt(3)' to 'unsigned _BitInt(2)' in initializer list}}
29+
_BitInt(3) bi8{ S2 };
30+
_BitInt(3) bi9{ U2 };
31+
_BitInt(3) bia{ S3 };
32+
_BitInt(3) bib{ U3 }; // expected-error {{non-constant-expression cannot be narrowed from type 'unsigned _BitInt(3)' to '_BitInt(3)' in initializer list}}
33+
unsigned _BitInt(3) bic{ S2 }; // expected-error {{non-constant-expression cannot be narrowed from type '_BitInt(2)' to 'unsigned _BitInt(3)' in initializer list}}
34+
unsigned _BitInt(3) bid{ U2 };
35+
unsigned _BitInt(3) bie{ S3 }; // expected-error {{non-constant-expression cannot be narrowed from type '_BitInt(3)' to 'unsigned _BitInt(3)' in initializer list}}
36+
unsigned _BitInt(3) bif{ U3 };

clang/www/cxx_dr_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15577,7 +15577,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
1557715577
<td><a href="https://cplusplus.github.io/CWG/issues/2627.html">2627</a></td>
1557815578
<td>C++23</td>
1557915579
<td>Bit-fields and narrowing conversions</td>
15580-
<td class="unknown" align="center">Unknown</td>
15580+
<td class="unreleased" align="center">Clang 20</td>
1558115581
</tr>
1558215582
<tr id="2628">
1558315583
<td><a href="https://cplusplus.github.io/CWG/issues/2628.html">2628</a></td>

0 commit comments

Comments
 (0)