Skip to content

Commit 12307d4

Browse files
committed
[clang] [SemaCXX] Implement CWG2627 Bit-fields and narrowing conversions
1 parent 5df2c00 commit 12307d4

File tree

5 files changed

+174
-42
lines changed

5 files changed

+174
-42
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ C++2c Feature Support
226226

227227
Resolutions to C++ Defect Reports
228228
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
229+
- Implemented `CWG2627 <https://wg21.link/CWG2627>`_ which means casts from
230+
a bit-field to an integral type is now not considered narrowing if the
231+
width of the bit-field means that all potential values are in the range
232+
of the target type, even if the type of the bit-field is larger.
229233

230234
C Language Changes
231235
------------------

clang/lib/Sema/SemaOverload.cpp

Lines changed: 65 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -433,61 +433,85 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
433433

434434
// -- from an integer type or unscoped enumeration type to an integer type
435435
// that cannot represent all the values of the original type, except where
436-
// the source is a constant expression and the actual value after
436+
// -- the source is a bit-field whose width w is less than that of its type
437+
// (or, for an enumeration type, its underlying type) and the target type
438+
// can represent all the values of a hypothetical extended integer type
439+
// with width w and with the same signedness as the original type or
440+
// -- the source is a constant expression and the actual value after
437441
// conversion will fit into the target type and will produce the original
438442
// value when converted back to the original type.
439443
case ICK_Integral_Conversion:
440444
IntegralConversion: {
441445
assert(FromType->isIntegralOrUnscopedEnumerationType());
442446
assert(ToType->isIntegralOrUnscopedEnumerationType());
443447
const bool FromSigned = FromType->isSignedIntegerOrEnumerationType();
444-
const unsigned FromWidth = Ctx.getIntWidth(FromType);
448+
unsigned FromWidth = Ctx.getIntWidth(FromType);
445449
const bool ToSigned = ToType->isSignedIntegerOrEnumerationType();
446450
const unsigned ToWidth = Ctx.getIntWidth(ToType);
447451

448-
if (FromWidth > ToWidth ||
449-
(FromWidth == ToWidth && FromSigned != ToSigned) ||
450-
(FromSigned && !ToSigned)) {
451-
// Not all values of FromType can be represented in ToType.
452-
const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted);
452+
constexpr auto CanRepresentAll = [](bool FromSigned, unsigned FromWidth,
453+
bool ToSigned, unsigned ToWidth) {
454+
return (FromWidth < ToWidth + (FromSigned == ToSigned)) &&
455+
(FromSigned <= ToSigned);
456+
};
453457

454-
// If it's value-dependent, we can't tell whether it's narrowing.
455-
if (Initializer->isValueDependent())
456-
return NK_Dependent_Narrowing;
458+
if (CanRepresentAll(FromSigned, FromWidth, ToSigned, ToWidth))
459+
return NK_Not_Narrowing;
457460

458-
std::optional<llvm::APSInt> OptInitializerValue;
459-
if (!(OptInitializerValue = Initializer->getIntegerConstantExpr(Ctx))) {
460-
// Such conversions on variables are always narrowing.
461-
return NK_Variable_Narrowing;
462-
}
463-
llvm::APSInt &InitializerValue = *OptInitializerValue;
464-
bool Narrowing = false;
465-
if (FromWidth < ToWidth) {
466-
// Negative -> unsigned is narrowing. Otherwise, more bits is never
467-
// narrowing.
468-
if (InitializerValue.isSigned() && InitializerValue.isNegative())
469-
Narrowing = true;
470-
} else {
471-
// Add a bit to the InitializerValue so we don't have to worry about
472-
// signed vs. unsigned comparisons.
473-
InitializerValue = InitializerValue.extend(
474-
InitializerValue.getBitWidth() + 1);
475-
// Convert the initializer to and from the target width and signed-ness.
476-
llvm::APSInt ConvertedValue = InitializerValue;
477-
ConvertedValue = ConvertedValue.trunc(ToWidth);
478-
ConvertedValue.setIsSigned(ToSigned);
479-
ConvertedValue = ConvertedValue.extend(InitializerValue.getBitWidth());
480-
ConvertedValue.setIsSigned(InitializerValue.isSigned());
481-
// If the result is different, this was a narrowing conversion.
482-
if (ConvertedValue != InitializerValue)
483-
Narrowing = true;
484-
}
485-
if (Narrowing) {
486-
ConstantType = Initializer->getType();
487-
ConstantValue = APValue(InitializerValue);
488-
return NK_Constant_Narrowing;
461+
// Not all values of FromType can be represented in ToType.
462+
const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted);
463+
464+
bool DependentBitField = false;
465+
if (const FieldDecl *BitField = Initializer->getSourceBitField()) {
466+
DependentBitField = BitField->getBitWidth()->isValueDependent();
467+
if (!DependentBitField) {
468+
FromWidth = BitField->getBitWidthValue(Ctx);
469+
if (CanRepresentAll(FromSigned, FromWidth, ToSigned, ToWidth))
470+
return NK_Not_Narrowing;
489471
}
490472
}
473+
474+
// If it's value-dependent, we can't tell whether it's narrowing.
475+
if (Initializer->isValueDependent())
476+
return NK_Dependent_Narrowing;
477+
478+
std::optional<llvm::APSInt> OptInitializerValue;
479+
if (!(OptInitializerValue = Initializer->getIntegerConstantExpr(Ctx))) {
480+
// If the source is a bit-field, this would only be narrowing if the width
481+
// ends up being too large
482+
if (DependentBitField)
483+
return NK_Dependent_Narrowing;
484+
// Otherwise, such a conversion is always narrowing
485+
return NK_Variable_Narrowing;
486+
}
487+
llvm::APSInt &InitializerValue = *OptInitializerValue;
488+
bool Narrowing = false;
489+
if (FromWidth < ToWidth) {
490+
// Negative -> unsigned is narrowing. Otherwise, more bits is never
491+
// narrowing.
492+
if (InitializerValue.isSigned() && InitializerValue.isNegative())
493+
Narrowing = true;
494+
} else {
495+
// Add a bit to the InitializerValue so we don't have to worry about
496+
// signed vs. unsigned comparisons.
497+
InitializerValue =
498+
InitializerValue.extend(InitializerValue.getBitWidth() + 1);
499+
// Convert the initializer to and from the target width and signed-ness.
500+
llvm::APSInt ConvertedValue = InitializerValue;
501+
ConvertedValue = ConvertedValue.trunc(ToWidth);
502+
ConvertedValue.setIsSigned(ToSigned);
503+
ConvertedValue = ConvertedValue.extend(InitializerValue.getBitWidth());
504+
ConvertedValue.setIsSigned(InitializerValue.isSigned());
505+
// If the result is different, this was a narrowing conversion.
506+
if (ConvertedValue != InitializerValue)
507+
Narrowing = true;
508+
}
509+
if (Narrowing) {
510+
ConstantType = Initializer->getType();
511+
ConstantValue = APValue(InitializerValue);
512+
return NK_Constant_Narrowing;
513+
}
514+
491515
return NK_Not_Narrowing;
492516
}
493517

clang/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,30 @@ void shrink_int() {
203203
unsigned short usc1 = { c }; // expected-error {{non-constant-expression cannot be narrowed from type 'signed char'}} expected-note {{silence}}
204204
unsigned short usc2 = { (signed char)'x' }; // OK
205205
unsigned short usc3 = { (signed char)-1 }; // expected-error {{ -1 which cannot be narrowed}} expected-note {{silence}}
206+
207+
#if __BITINT_MAXWIDTH__ >= 3
208+
_BitInt(2) S2 = 0;
209+
unsigned _BitInt(2) U2 = 0;
210+
_BitInt(3) S3 = 0;
211+
unsigned _BitInt(3) U3 = 0;
212+
213+
_BitInt(2) bi0 = { S2 };
214+
_BitInt(2) bi1 = { U2 }; // expected-error {{cannot be narrowed from type 'unsigned _BitInt(2)'}}
215+
_BitInt(2) bi2 = { S3 }; // expected-error {{cannot be narrowed from type '_BitInt(3)'}}
216+
_BitInt(2) bi3 = { U3 }; // expected-error {{cannot be narrowed from type 'unsigned _BitInt(3)'}}
217+
unsigned _BitInt(2) bi4 = { S2 }; // expected-error {{cannot be narrowed from type '_BitInt(2)'}}
218+
unsigned _BitInt(2) bi5 = { U2 };
219+
unsigned _BitInt(2) bi6 = { S3 }; // expected-error {{cannot be narrowed from type '_BitInt(3)'}}
220+
unsigned _BitInt(2) bi7 = { U3 }; // expected-error {{cannot be narrowed from type 'unsigned _BitInt(3)'}}
221+
_BitInt(3) bi8 = { S2 };
222+
_BitInt(3) bi9 = { U2 };
223+
_BitInt(3) bia = { S3 };
224+
_BitInt(3) bib = { U3 }; // expected-error {{cannot be narrowed from type 'unsigned _BitInt(3)'}}
225+
unsigned _BitInt(3) bic = { S2 }; // expected-error {{cannot be narrowed from type '_BitInt(2)'}}
226+
unsigned _BitInt(3) bid = { U2 };
227+
unsigned _BitInt(3) bie = { S3 }; // expected-error {{cannot be narrowed from type '_BitInt(3)'}}
228+
unsigned _BitInt(3) bif = { U3 };
229+
#endif
206230
}
207231

208232
// Be sure that type- and value-dependent expressions in templates get the error

clang/test/CXX/drs/dr26xx.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@
66
// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx20,since-cxx23
77
// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx20,since-cxx23
88

9+
namespace std {
10+
#if __cplusplus >= 202002L
11+
struct strong_ordering {
12+
int n;
13+
constexpr operator int() const { return n; }
14+
static const strong_ordering less, equal, greater;
15+
};
16+
constexpr strong_ordering strong_ordering::less{-1},
17+
strong_ordering::equal{0}, strong_ordering::greater{1};
18+
#endif
19+
20+
typedef __INT16_TYPE__ int16_t;
21+
typedef __UINT16_TYPE__ uint16_t;
22+
typedef __INT32_TYPE__ int32_t;
23+
typedef __UINT32_TYPE__ uint32_t;
24+
typedef __INT64_TYPE__ int64_t;
25+
typedef __UINT64_TYPE__ uint64_t;
26+
}
927

1028
namespace dr2621 { // dr2621: 16
1129
#if __cplusplus >= 202002L
@@ -24,6 +42,68 @@ using enum E;
2442
#endif
2543
}
2644

45+
namespace dr2627 { // dr2627: 18
46+
#if __cplusplus >= 202002L
47+
struct C {
48+
long long i : 8;
49+
friend auto operator<=>(C, C) = default;
50+
};
51+
52+
void f() {
53+
C x{1}, y{2};
54+
static_cast<void>(x <=> y);
55+
static_cast<void>(x.i <=> y.i);
56+
}
57+
#endif
58+
59+
#if __cplusplus >= 201103L
60+
template<int W>
61+
struct D {
62+
__int128 i : W;
63+
};
64+
65+
template<int W>
66+
std::int64_t f(D<W> d) {
67+
return std::int64_t{ d.i }; // #dr2627-f
68+
}
69+
70+
template std::int64_t f(D<63>);
71+
template std::int64_t f(D<64>);
72+
template std::int64_t f(D<65>);
73+
// since-cxx11-error-re@#dr2627-f {{non-constant-expression cannot be narrowed from type '__int128' to 'std::int64_t' (aka '{{.+}}') in initializer list}}
74+
// since-cxx11-note@-2 {{in instantiation of function template specialization 'dr2627::f<65>' requested here}}
75+
// since-cxx11-note@#dr2627-f {{insert an explicit cast to silence this issue}}
76+
77+
template<typename Target, typename Source>
78+
Target g(Source x) {
79+
return Target{ x.i }; // #dr2627-g
80+
}
81+
82+
template<typename T, int N>
83+
struct E {
84+
T i : N;
85+
};
86+
87+
template std::int16_t g(E<int, 16>);
88+
template std::int16_t g(E<unsigned, 15>);
89+
template std::int16_t g(E<unsigned, 16>);
90+
// since-cxx11-error-re@#dr2627-g {{non-constant-expression cannot be narrowed from type 'unsigned int' to '{{.+}}' in initializer list}}
91+
// since-cxx11-note-re@-2 {{in instantiation of function template specialization 'dr2627::g<{{.+}}, dr2627::E<unsigned int, 16>>' requested here}}
92+
// since-cxx11-note@#dr2627-g {{insert an explicit cast to silence this issue}}
93+
template std::uint16_t g(E<unsigned, 16>);
94+
template std::uint16_t g(E<int, 1>);
95+
// since-cxx11-error-re@#dr2627-g {{non-constant-expression cannot be narrowed from type 'int' to '{{.+}}' in initializer list}}
96+
// since-cxx11-note-re@-2 {{in instantiation of function template specialization 'dr2627::g<{{.+}}, dr2627::E<int, 1>>' requested here}}
97+
// since-cxx11-note@#dr2627-g {{insert an explicit cast to silence this issue}}
98+
99+
template bool g(E<unsigned, 1>);
100+
template bool g(E<int, 1>);
101+
// since-cxx11-error@#dr2627-g {{non-constant-expression cannot be narrowed from type 'int' to 'bool' in initializer list}}
102+
// since-cxx11-note@-2 {{in instantiation of function template specialization 'dr2627::g<bool, dr2627::E<int, 1>>' requested here}}
103+
// since-cxx11-note@#dr2627-g {{insert an explicit cast to silence this issue}}
104+
#endif
105+
}
106+
27107
namespace dr2628 { // dr2628: no
28108
// this was reverted for the 16.x release
29109
// due to regressions, see the issue for more details:

clang/www/cxx_dr_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15570,7 +15570,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
1557015570
<td><a href="https://cplusplus.github.io/CWG/issues/2627.html">2627</a></td>
1557115571
<td>C++23</td>
1557215572
<td>Bit-fields and narrowing conversions</td>
15573-
<td class="unknown" align="center">Unknown</td>
15573+
<td class="unreleased" align="center">Clang 18</td>
1557415574
</tr>
1557515575
<tr id="2628">
1557615576
<td><a href="https://cplusplus.github.io/CWG/issues/2628.html">2628</a></td>

0 commit comments

Comments
 (0)