Skip to content

Commit 92f8720

Browse files
committed
[clang] [SemaCXX] Implement CWG2627 Bit-fields and narrowing conversions
1 parent 0620a63 commit 92f8720

File tree

9 files changed

+278
-48
lines changed

9 files changed

+278
-48
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,11 @@ Resolutions to C++ Defect Reports
157157
- Clang now diagnoses declarative nested-name-specifiers with pack-index-specifiers.
158158
(`CWG2858: Declarative nested-name-specifiers and pack-index-specifiers <https://cplusplus.github.io/CWG/issues/2858.html>`_).
159159

160+
- Casts from a bit-field to an integral type is now not considered narrowing if the
161+
width of the bit-field means that all potential values are in the range
162+
of the target type, even if the type of the bit-field is larger.
163+
(`CWG2627. Bit-fields and narrowing conversions <https://cplusplus.github.io/CWG/issues/2627.html>`_).
164+
160165
C Language Changes
161166
------------------
162167

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6253,6 +6253,9 @@ def ext_init_list_variable_narrowing_const_reference : ExtWarn<
62536253
def ext_init_list_constant_narrowing : ExtWarn<
62546254
"constant expression evaluates to %0 which cannot be narrowed to type %1">,
62556255
InGroup<CXX11Narrowing>, DefaultError, SFINAEFailure;
6256+
def ext_bit_field_narrowing : Extension<
6257+
"narrowing non-constant-expression from %0 bit-field of width %2 to %1 is a C++23 extension">,
6258+
InGroup<CXX20CompatPedantic>, SFINAEFailure;
62566259
def ext_init_list_constant_narrowing_const_reference : ExtWarn<
62576260
ext_init_list_constant_narrowing.Summary>,
62586261
InGroup<CXX11NarrowingConstReference>, DefaultError, SFINAEFailure;

clang/include/clang/Sema/Overload.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,11 @@ class Sema;
244244
/// Not a narrowing conversion.
245245
NK_Not_Narrowing,
246246

247-
/// A narrowing conversion by virtue of the source and destination types.
247+
/// Not a narrowing conversion in C++23 because the source is a bit-field
248+
/// whose range can fit in the target type
249+
NK_BitField_Not_Narrowing,
250+
251+
/// A narrowing conversion by virtue of the source and target types.
248252
NK_Type_Narrowing,
249253

250254
/// A narrowing conversion, because a constant expression got narrowed.
@@ -387,6 +391,7 @@ class Sema;
387391
NarrowingKind
388392
getNarrowingKind(ASTContext &Context, const Expr *Converted,
389393
APValue &ConstantValue, QualType &ConstantType,
394+
unsigned &BitFieldWidth,
390395
bool IgnoreFloatToIntegralConversion = false) const;
391396
bool isPointerConversionToBool() const;
392397
bool isPointerConversionToVoidPointer(ASTContext& Context) const;

clang/lib/Sema/SemaExpr.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12361,15 +12361,23 @@ static bool checkThreeWayNarrowingConversion(Sema &S, QualType ToType, Expr *E,
1236112361

1236212362
APValue PreNarrowingValue;
1236312363
QualType PreNarrowingType;
12364+
unsigned BitFieldWidth;
1236412365
switch (SCS.getNarrowingKind(S.Context, E, PreNarrowingValue,
12365-
PreNarrowingType,
12366+
PreNarrowingType, BitFieldWidth,
1236612367
/*IgnoreFloatToIntegralConversion*/ true)) {
1236712368
case NK_Dependent_Narrowing:
1236812369
// Implicit conversion to a narrower type, but the expression is
1236912370
// value-dependent so we can't tell whether it's actually narrowing.
1237012371
case NK_Not_Narrowing:
1237112372
return false;
1237212373

12374+
case NK_BitField_Not_Narrowing:
12375+
if (!S.getLangOpts().CPlusPlus23) {
12376+
return S.Diag(E->getBeginLoc(), diag::ext_bit_field_narrowing)
12377+
<< FromType << ToType << BitFieldWidth;
12378+
}
12379+
return false;
12380+
1237312381
case NK_Constant_Narrowing:
1237412382
// Implicit conversion to a narrower type, and the value is not a constant
1237512383
// expression.

clang/lib/Sema/SemaInit.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10509,13 +10509,27 @@ static void DiagnoseNarrowingInInitList(Sema &S,
1050910509
// C++11 [dcl.init.list]p7: Check whether this is a narrowing conversion.
1051010510
APValue ConstantValue;
1051110511
QualType ConstantType;
10512+
unsigned BitFieldWidth;
1051210513
switch (SCS->getNarrowingKind(S.Context, PostInit, ConstantValue,
10513-
ConstantType)) {
10514+
ConstantType, BitFieldWidth)) {
1051410515
case NK_Not_Narrowing:
1051510516
case NK_Dependent_Narrowing:
1051610517
// No narrowing occurred.
1051710518
return;
1051810519

10520+
case NK_BitField_Not_Narrowing:
10521+
// A bit-field was "narrowed" to an integer type which is wider than the
10522+
// bit-field but the declared type of the bit-field is wider than the target
10523+
// type. This is not considered narrowing in C++23.
10524+
if (S.getLangOpts().CPlusPlus23)
10525+
return;
10526+
if (!(S.Diag(PostInit->getBeginLoc(), diag::ext_bit_field_narrowing)
10527+
<< PreNarrowingType
10528+
<< EntityType.getNonReferenceType().getLocalUnqualifiedType()
10529+
<< BitFieldWidth))
10530+
return;
10531+
break;
10532+
1051910533
case NK_Type_Narrowing: {
1052010534
// This was a floating-to-integer conversion, which is always considered a
1052110535
// narrowing conversion even if the value is a constant and can be
@@ -10592,9 +10606,10 @@ static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType,
1059210606

1059310607
APValue Value;
1059410608
QualType PreNarrowingType;
10609+
unsigned BitFieldWidth;
1059510610
// Reuse C++ narrowing check.
1059610611
switch (ICS.Standard.getNarrowingKind(
10597-
S.Context, Init, Value, PreNarrowingType,
10612+
S.Context, Init, Value, PreNarrowingType, BitFieldWidth,
1059810613
/*IgnoreFloatToIntegralConversion*/ false)) {
1059910614
// The value doesn't fit.
1060010615
case NK_Constant_Narrowing:
@@ -10610,6 +10625,7 @@ static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType,
1061010625

1061110626
// Since we only reuse narrowing check for C23 constexpr variables here, we're
1061210627
// not really interested in these cases.
10628+
case NK_BitField_Not_Narrowing:
1061310629
case NK_Dependent_Narrowing:
1061410630
case NK_Variable_Narrowing:
1061510631
case NK_Not_Narrowing:

clang/lib/Sema/SemaOverload.cpp

Lines changed: 76 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -331,11 +331,14 @@ static const Expr *IgnoreNarrowingConversion(ASTContext &Ctx,
331331
/// value of the expression prior to the narrowing conversion.
332332
/// \param ConstantType If this is an NK_Constant_Narrowing conversion, the
333333
/// type of the expression prior to the narrowing conversion.
334+
/// \param BitFieldWidth If this is an NK_BitField_Not_Narrowing conversion,
335+
/// the width of the source bit-field.
334336
/// \param IgnoreFloatToIntegralConversion If true type-narrowing conversions
335337
/// from floating point types to integral types should be ignored.
336338
NarrowingKind StandardConversionSequence::getNarrowingKind(
337339
ASTContext &Ctx, const Expr *Converted, APValue &ConstantValue,
338-
QualType &ConstantType, bool IgnoreFloatToIntegralConversion) const {
340+
QualType &ConstantType, unsigned &BitFieldWidth,
341+
bool IgnoreFloatToIntegralConversion) const {
339342
assert((Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23) &&
340343
"narrowing check outside C++");
341344

@@ -463,7 +466,12 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
463466

464467
// -- from an integer type or unscoped enumeration type to an integer type
465468
// that cannot represent all the values of the original type, except where
466-
// the source is a constant expression and the actual value after
469+
// (C++23) -- the source is a bit-field whose width w is less than that of
470+
// its type (or, for an enumeration type, its underlying type) and the
471+
// target type can represent all the values of a hypothetical extended
472+
// integer type with width w and with the same signedness as the original
473+
// type or
474+
// -- the source is a constant expression and the actual value after
467475
// conversion will fit into the target type and will produce the original
468476
// value when converted back to the original type.
469477
case ICK_Integral_Conversion:
@@ -475,49 +483,70 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
475483
const bool ToSigned = ToType->isSignedIntegerOrEnumerationType();
476484
const unsigned ToWidth = Ctx.getIntWidth(ToType);
477485

478-
if (FromWidth > ToWidth ||
479-
(FromWidth == ToWidth && FromSigned != ToSigned) ||
480-
(FromSigned && !ToSigned)) {
481-
// Not all values of FromType can be represented in ToType.
482-
const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted);
483-
484-
// If it's value-dependent, we can't tell whether it's narrowing.
485-
if (Initializer->isValueDependent())
486-
return NK_Dependent_Narrowing;
486+
constexpr auto CanRepresentAll = [](bool FromSigned, unsigned FromWidth,
487+
bool ToSigned, unsigned ToWidth) {
488+
return (FromWidth < ToWidth + (FromSigned == ToSigned)) &&
489+
(FromSigned <= ToSigned);
490+
};
487491

488-
std::optional<llvm::APSInt> OptInitializerValue;
489-
if (!(OptInitializerValue = Initializer->getIntegerConstantExpr(Ctx))) {
490-
// Such conversions on variables are always narrowing.
491-
return NK_Variable_Narrowing;
492-
}
493-
llvm::APSInt &InitializerValue = *OptInitializerValue;
494-
bool Narrowing = false;
495-
if (FromWidth < ToWidth) {
496-
// Negative -> unsigned is narrowing. Otherwise, more bits is never
497-
// narrowing.
498-
if (InitializerValue.isSigned() && InitializerValue.isNegative())
499-
Narrowing = true;
500-
} else {
501-
// Add a bit to the InitializerValue so we don't have to worry about
502-
// signed vs. unsigned comparisons.
503-
InitializerValue = InitializerValue.extend(
504-
InitializerValue.getBitWidth() + 1);
505-
// Convert the initializer to and from the target width and signed-ness.
506-
llvm::APSInt ConvertedValue = InitializerValue;
507-
ConvertedValue = ConvertedValue.trunc(ToWidth);
508-
ConvertedValue.setIsSigned(ToSigned);
509-
ConvertedValue = ConvertedValue.extend(InitializerValue.getBitWidth());
510-
ConvertedValue.setIsSigned(InitializerValue.isSigned());
511-
// If the result is different, this was a narrowing conversion.
512-
if (ConvertedValue != InitializerValue)
513-
Narrowing = true;
514-
}
515-
if (Narrowing) {
516-
ConstantType = Initializer->getType();
517-
ConstantValue = APValue(InitializerValue);
518-
return NK_Constant_Narrowing;
492+
if (CanRepresentAll(FromSigned, FromWidth, ToSigned, ToWidth))
493+
return NK_Not_Narrowing;
494+
495+
// Not all values of FromType can be represented in ToType.
496+
const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted);
497+
498+
// If it's value-dependent, we can't tell whether it's narrowing.
499+
if (Initializer->isValueDependent())
500+
return NK_Dependent_Narrowing;
501+
502+
std::optional<llvm::APSInt> OptInitializerValue;
503+
if (!(OptInitializerValue = Initializer->getIntegerConstantExpr(Ctx))) {
504+
// Check for bit-field whose width means that this would not be narrowing
505+
// (This check is after checking for constant expressions because
506+
// a constant expression that fits is never narrowing but a non-constant
507+
// expression that comes from a bit-field is only not narrowing before
508+
// C++23 as an extension)
509+
if (const FieldDecl *BitField = Initializer->getSourceBitField()) {
510+
if (BitField->getBitWidth()->isValueDependent()) {
511+
return NK_Dependent_Narrowing;
512+
}
513+
BitFieldWidth = BitField->getBitWidthValue(Ctx);
514+
if (CanRepresentAll(FromSigned, BitFieldWidth, ToSigned, ToWidth)) {
515+
return NK_BitField_Not_Narrowing;
516+
}
519517
}
518+
519+
// Otherwise, such a conversion is always narrowing
520+
return NK_Variable_Narrowing;
521+
}
522+
llvm::APSInt &InitializerValue = *OptInitializerValue;
523+
bool Narrowing = false;
524+
if (FromWidth < ToWidth) {
525+
// Negative -> unsigned is narrowing. Otherwise, more bits is never
526+
// narrowing.
527+
if (InitializerValue.isSigned() && InitializerValue.isNegative())
528+
Narrowing = true;
529+
} else {
530+
// Add a bit to the InitializerValue so we don't have to worry about
531+
// signed vs. unsigned comparisons.
532+
InitializerValue =
533+
InitializerValue.extend(InitializerValue.getBitWidth() + 1);
534+
// Convert the initializer to and from the target width and signed-ness.
535+
llvm::APSInt ConvertedValue = InitializerValue;
536+
ConvertedValue = ConvertedValue.trunc(ToWidth);
537+
ConvertedValue.setIsSigned(ToSigned);
538+
ConvertedValue = ConvertedValue.extend(InitializerValue.getBitWidth());
539+
ConvertedValue.setIsSigned(InitializerValue.isSigned());
540+
// If the result is different, this was a narrowing conversion.
541+
if (ConvertedValue != InitializerValue)
542+
Narrowing = true;
543+
}
544+
if (Narrowing) {
545+
ConstantType = Initializer->getType();
546+
ConstantValue = APValue(InitializerValue);
547+
return NK_Constant_Narrowing;
520548
}
549+
521550
return NK_Not_Narrowing;
522551
}
523552
case ICK_Complex_Real:
@@ -6232,14 +6261,18 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From,
62326261
// Check for a narrowing implicit conversion.
62336262
bool ReturnPreNarrowingValue = false;
62346263
QualType PreNarrowingType;
6264+
unsigned BitFieldWidth;
62356265
switch (SCS->getNarrowingKind(S.Context, Result.get(), PreNarrowingValue,
6236-
PreNarrowingType)) {
6266+
PreNarrowingType, BitFieldWidth)) {
62376267
case NK_Dependent_Narrowing:
62386268
// Implicit conversion to a narrower type, but the expression is
62396269
// value-dependent so we can't tell whether it's actually narrowing.
62406270
case NK_Variable_Narrowing:
62416271
// Implicit conversion to a narrower type, and the value is not a constant
62426272
// expression. We'll diagnose this in a moment.
6273+
case NK_BitField_Not_Narrowing:
6274+
// Implicit conversion where the source is a bit-field and not a constant
6275+
// expression.
62436276
case NK_Not_Narrowing:
62446277
break;
62456278

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

0 commit comments

Comments
 (0)