Skip to content

Commit dbffbf5

Browse files
committed
C++: Add support for C23 _BitInts
1 parent 4812cdd commit dbffbf5

3 files changed

Lines changed: 137 additions & 50 deletions

File tree

‎cpp/ql/lib/semmle/code/cpp/Type.qll‎

Lines changed: 125 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ class UnknownType extends BuiltInType {
349349
override string getAPrimaryQlClass() { result = "UnknownType" }
350350
}
351351

352-
private predicate isArithmeticType(@builtintype type, int kind) {
352+
private predicate isBuiltInArithmeticType(@builtintype type, int kind) {
353353
builtintypes(type, _, kind, _, _, _) and
354354
kind >= 4 and
355355
kind != 34 and // Exclude decltype(nullptr)
@@ -378,14 +378,49 @@ class ScalableVectorCount extends BuiltInType {
378378
* operations may be performed. Examples of arithmetic types include
379379
* `char`, `int`, `float`, and `bool`.
380380
*/
381-
class ArithmeticType extends BuiltInType {
382-
ArithmeticType() { isArithmeticType(underlyingElement(this), _) }
383-
381+
abstract private class ArithmeticTypeImpl extends Type {
384382
override string getAPrimaryQlClass() { result = "ArithmeticType" }
385383
}
386384

387-
private predicate isIntegralType(@builtintype type, int kind) {
388-
isArithmeticType(type, kind) and
385+
final class ArithmeticType = ArithmeticTypeImpl;
386+
387+
/**
388+
* A C/C++ arithmetic type that is not a bit-precise integer.
389+
*/
390+
class BuiltInArithmeticType extends Type, BuiltInType {
391+
BuiltInArithmeticType() { isBuiltInArithmeticType(underlyingElement(this), _) }
392+
}
393+
394+
/**
395+
* A C23 bit-precise integer (`_BitInt`).
396+
*/
397+
class BitPreciseArithmeticType extends Type {
398+
boolean isSigned;
399+
int bitWidth;
400+
401+
BitPreciseArithmeticType() { bit_precise_types(underlyingElement(this), isSigned, bitWidth) }
402+
403+
override string toString() { result = this.getName() }
404+
405+
override string getName() {
406+
isSigned = true and result = "_BitInt(" + bitWidth + ")"
407+
or
408+
isSigned = false and result = "unsigned _BitInt(" + bitWidth + ")"
409+
}
410+
411+
override string explain() { result = this.getName() }
412+
413+
override predicate isDeeplyConstBelow() { any() } // No subparts
414+
415+
/* Holds if this bit-precise type is signed. */
416+
predicate isSigned() { isSigned = true }
417+
418+
/* Yields the bit width of the bit-precise type */
419+
int getBitWith() { result = bitWidth }
420+
}
421+
422+
private predicate isBuiltInIntegralType(@builtintype type, int kind) {
423+
isBuiltInArithmeticType(type, kind) and
389424
(
390425
kind < 24
391426
or
@@ -420,10 +455,13 @@ private predicate isIntegralType(@builtintype type, int kind) {
420455
class IntegralOrEnumType extends Type {
421456
IntegralOrEnumType() {
422457
// Integral type
423-
isIntegralType(underlyingElement(this), _)
458+
isBuiltInIntegralType(underlyingElement(this), _)
424459
or
425460
// Enum type
426461
usertypes(underlyingElement(this), _, [4, 13])
462+
or
463+
// Bit-precise integer type (`_BitInt`)
464+
bit_precise_types(underlyingElement(this), _, _)
427465
}
428466
}
429467

@@ -499,19 +537,54 @@ private predicate integralTypeMapping(int original, int canonical, int unsigned,
499537
* char c;
500538
* ```
501539
*/
502-
class IntegralType extends ArithmeticType, IntegralOrEnumType {
503-
int kind;
504-
505-
IntegralType() { isIntegralType(underlyingElement(this), kind) }
506-
540+
abstract private class IntegralTypeImpl extends ArithmeticTypeImpl, IntegralOrEnumType {
507541
/** Holds if this integral type is signed. */
508-
predicate isSigned() { builtintypes(underlyingElement(this), _, _, _, -1, _) }
542+
abstract predicate isSigned();
509543

510544
/** Holds if this integral type is unsigned. */
511-
predicate isUnsigned() { builtintypes(underlyingElement(this), _, _, _, 1, _) }
545+
abstract predicate isUnsigned();
512546

513547
/** Holds if this integral type is explicitly signed. */
514-
predicate isExplicitlySigned() {
548+
abstract predicate isExplicitlySigned();
549+
550+
/** Holds if this integral type is explicitly unsigned. */
551+
abstract predicate isExplicitlyUnsigned();
552+
553+
/** Holds if this integral type is implicitly signed. */
554+
abstract predicate isImplicitlySigned();
555+
556+
/**
557+
* Gets the unsigned type corresponding to this integral type. For
558+
* example on a `short`, this would give the type `unsigned short`.
559+
*/
560+
abstract IntegralType getUnsigned();
561+
562+
/**
563+
* Gets the canonical type corresponding to this integral type.
564+
*
565+
* For a plain type, this gives the same type (e.g. `short` -> `short`).
566+
* For an explicitly unsigned type, this gives the same type (e.g. `unsigned short` -> `unsigned short`).
567+
* For an explicitly signed type, this gives the plain version of that type (e.g. `signed short` -> `short`), except
568+
* that `signed char` -> `signed char`.
569+
*/
570+
abstract IntegralType getCanonicalArithmeticType();
571+
}
572+
573+
final class IntegralType = IntegralTypeImpl;
574+
575+
/**
576+
* A C/C++ integral type that is not a bit-precise integer.
577+
*/
578+
class BuiltInIntegralType extends IntegralTypeImpl, BuiltInArithmeticType {
579+
int kind;
580+
581+
BuiltInIntegralType() { isBuiltInIntegralType(underlyingElement(this), kind) }
582+
583+
override predicate isSigned() { builtintypes(underlyingElement(this), _, _, _, -1, _) }
584+
585+
override predicate isUnsigned() { builtintypes(underlyingElement(this), _, _, _, 1, _) }
586+
587+
override predicate isExplicitlySigned() {
515588
builtintypes(underlyingElement(this), _, 7, _, _, _) or
516589
builtintypes(underlyingElement(this), _, 10, _, _, _) or
517590
builtintypes(underlyingElement(this), _, 13, _, _, _) or
@@ -520,8 +593,7 @@ class IntegralType extends ArithmeticType, IntegralOrEnumType {
520593
builtintypes(underlyingElement(this), _, 37, _, _, _)
521594
}
522595

523-
/** Holds if this integral type is explicitly unsigned. */
524-
predicate isExplicitlyUnsigned() {
596+
override predicate isExplicitlyUnsigned() {
525597
builtintypes(underlyingElement(this), _, 6, _, _, _) or
526598
builtintypes(underlyingElement(this), _, 9, _, _, _) or
527599
builtintypes(underlyingElement(this), _, 12, _, _, _) or
@@ -530,8 +602,7 @@ class IntegralType extends ArithmeticType, IntegralOrEnumType {
530602
builtintypes(underlyingElement(this), _, 36, _, _, _)
531603
}
532604

533-
/** Holds if this integral type is implicitly signed. */
534-
predicate isImplicitlySigned() {
605+
override predicate isImplicitlySigned() {
535606
builtintypes(underlyingElement(this), _, 5, _, -1, _) or
536607
builtintypes(underlyingElement(this), _, 8, _, -1, _) or
537608
builtintypes(underlyingElement(this), _, 11, _, -1, _) or
@@ -540,33 +611,42 @@ class IntegralType extends ArithmeticType, IntegralOrEnumType {
540611
builtintypes(underlyingElement(this), _, 35, _, -1, _)
541612
}
542613

543-
/**
544-
* Gets the unsigned type corresponding to this integral type. For
545-
* example on a `short`, this would give the type `unsigned short`.
546-
*/
547-
IntegralType getUnsigned() {
614+
override IntegralType getUnsigned() {
548615
exists(int unsignedKind |
549616
integralTypeMapping(kind, _, unsignedKind, _) and
550617
builtintypes(unresolveElement(result), _, unsignedKind, _, _, _)
551618
)
552619
}
553620

554-
/**
555-
* Gets the canonical type corresponding to this integral type.
556-
*
557-
* For a plain type, this gives the same type (e.g. `short` -> `short`).
558-
* For an explicitly unsigned type, this gives the same type (e.g. `unsigned short` -> `unsigned short`).
559-
* For an explicitly signed type, this gives the plain version of that type (e.g. `signed short` -> `short`), except
560-
* that `signed char` -> `signed char`.
561-
*/
562-
IntegralType getCanonicalArithmeticType() {
621+
override IntegralType getCanonicalArithmeticType() {
563622
exists(int canonicalKind |
564623
integralTypeMapping(kind, canonicalKind, _, _) and
565624
builtintypes(unresolveElement(result), _, canonicalKind, _, _, _)
566625
)
567626
}
568627
}
569628

629+
/**
630+
* A C23 bit-precise integer (`_BitInt`).
631+
*/
632+
class BitPreciseIntegralType extends IntegralTypeImpl, BitPreciseArithmeticType {
633+
override predicate isSigned() { this.(BitPreciseArithmeticType).isSigned() }
634+
635+
override predicate isUnsigned() { not this.isSigned() }
636+
637+
override predicate isExplicitlySigned() { this.isSigned() }
638+
639+
override predicate isExplicitlyUnsigned() { this.isUnsigned() }
640+
641+
override predicate isImplicitlySigned() { none() }
642+
643+
override IntegralType getUnsigned() {
644+
bit_precise_types(unresolveElement(result), false, this.(BitPreciseArithmeticType).getBitWith())
645+
}
646+
647+
override IntegralType getCanonicalArithmeticType() { result = this }
648+
}
649+
570650
/**
571651
* The C/C++ boolean type. See 4.2. This is the C `_Bool` type
572652
* or the C++ `bool` type. For example:
@@ -575,7 +655,7 @@ class IntegralType extends ArithmeticType, IntegralOrEnumType {
575655
* _Bool c, d; // C
576656
* ```
577657
*/
578-
class BoolType extends IntegralType {
658+
class BoolType extends BuiltInIntegralType {
579659
BoolType() { builtintypes(underlyingElement(this), _, 4, _, _, _) }
580660

581661
override string getAPrimaryQlClass() { result = "BoolType" }
@@ -591,7 +671,7 @@ class BoolType extends IntegralType {
591671
* unsigned char e, f;
592672
* ```
593673
*/
594-
class CharType extends IntegralType {
674+
class CharType extends BuiltInIntegralType {
595675
CharType() { builtintypes(underlyingElement(this), _, [5, 6, 7], _, _, _) }
596676
}
597677

@@ -641,7 +721,7 @@ class SignedCharType extends CharType {
641721
* signed short ss;
642722
* ```
643723
*/
644-
class ShortType extends IntegralType {
724+
class ShortType extends BuiltInIntegralType {
645725
ShortType() {
646726
builtintypes(underlyingElement(this), _, 8, _, _, _) or
647727
builtintypes(underlyingElement(this), _, 9, _, _, _) or
@@ -658,7 +738,7 @@ class ShortType extends IntegralType {
658738
* unsigned int ui;
659739
* ```
660740
*/
661-
class IntType extends IntegralType {
741+
class IntType extends BuiltInIntegralType {
662742
IntType() {
663743
builtintypes(underlyingElement(this), _, 11, _, _, _) or
664744
builtintypes(underlyingElement(this), _, 12, _, _, _) or
@@ -675,7 +755,7 @@ class IntType extends IntegralType {
675755
* long l;
676756
* ```
677757
*/
678-
class LongType extends IntegralType {
758+
class LongType extends BuiltInIntegralType {
679759
LongType() {
680760
builtintypes(underlyingElement(this), _, 14, _, _, _) or
681761
builtintypes(underlyingElement(this), _, 15, _, _, _) or
@@ -692,7 +772,7 @@ class LongType extends IntegralType {
692772
* signed long long sll;
693773
* ```
694774
*/
695-
class LongLongType extends IntegralType {
775+
class LongLongType extends BuiltInIntegralType {
696776
LongLongType() {
697777
builtintypes(underlyingElement(this), _, 17, _, _, _) or
698778
builtintypes(underlyingElement(this), _, 18, _, _, _) or
@@ -710,7 +790,7 @@ class LongLongType extends IntegralType {
710790
* unsigned __int128 ui128;
711791
* ```
712792
*/
713-
class Int128Type extends IntegralType {
793+
class Int128Type extends BuiltInIntegralType {
714794
Int128Type() {
715795
builtintypes(underlyingElement(this), _, 35, _, _, _) or
716796
builtintypes(underlyingElement(this), _, 36, _, _, _) or
@@ -865,7 +945,7 @@ private predicate floatingPointTypeMapping(
865945
* fixed-size floating-point types like `_Float32`, and the extended-precision floating-point types
866946
* like `_Float64x`. It also includes the complex and imaginary versions of all of these types.
867947
*/
868-
class FloatingPointType extends ArithmeticType {
948+
class FloatingPointType extends ArithmeticTypeImpl, BuiltInType {
869949
final int base;
870950
final TypeDomain domain;
871951
final int realKind;
@@ -1003,7 +1083,7 @@ class VoidType extends BuiltInType {
10031083
* wchar_t wc;
10041084
* ```
10051085
*/
1006-
class WideCharType extends IntegralType {
1086+
class WideCharType extends BuiltInIntegralType {
10071087
WideCharType() { builtintypes(underlyingElement(this), _, 33, _, _, _) }
10081088

10091089
override string getAPrimaryQlClass() { result = "WideCharType" }
@@ -1015,7 +1095,7 @@ class WideCharType extends IntegralType {
10151095
* char8_t c8;
10161096
* ```
10171097
*/
1018-
class Char8Type extends IntegralType {
1098+
class Char8Type extends BuiltInIntegralType {
10191099
Char8Type() { builtintypes(underlyingElement(this), _, 51, _, _, _) }
10201100

10211101
override string getAPrimaryQlClass() { result = "Char8Type" }
@@ -1027,7 +1107,7 @@ class Char8Type extends IntegralType {
10271107
* char16_t c16;
10281108
* ```
10291109
*/
1030-
class Char16Type extends IntegralType {
1110+
class Char16Type extends BuiltInIntegralType {
10311111
Char16Type() { builtintypes(underlyingElement(this), _, 43, _, _, _) }
10321112

10331113
override string getAPrimaryQlClass() { result = "Char16Type" }
@@ -1039,7 +1119,7 @@ class Char16Type extends IntegralType {
10391119
* char32_t c32;
10401120
* ```
10411121
*/
1042-
class Char32Type extends IntegralType {
1122+
class Char32Type extends BuiltInIntegralType {
10431123
Char32Type() { builtintypes(underlyingElement(this), _, 44, _, _, _) }
10441124

10451125
override string getAPrimaryQlClass() { result = "Char32Type" }

‎cpp/ql/lib/semmle/code/cpp/commons/CommonType.qll‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ class Wchar_t extends Type {
372372
*
373373
* This class is meaningless if a Microsoft compiler was not used.
374374
*/
375-
class MicrosoftInt8Type extends IntegralType {
375+
class MicrosoftInt8Type extends BuiltInIntegralType {
376376
MicrosoftInt8Type() {
377377
this instanceof CharType and
378378
not this.isExplicitlyUnsigned() and
@@ -388,7 +388,7 @@ class MicrosoftInt8Type extends IntegralType {
388388
*
389389
* This class is meaningless if a Microsoft compiler was not used.
390390
*/
391-
class MicrosoftInt16Type extends IntegralType {
391+
class MicrosoftInt16Type extends BuiltInIntegralType {
392392
MicrosoftInt16Type() {
393393
this instanceof ShortType and
394394
not this.isExplicitlyUnsigned() and
@@ -404,7 +404,7 @@ class MicrosoftInt16Type extends IntegralType {
404404
*
405405
* This class is meaningless if a Microsoft compiler was not used.
406406
*/
407-
class MicrosoftInt32Type extends IntegralType {
407+
class MicrosoftInt32Type extends BuiltInIntegralType {
408408
MicrosoftInt32Type() {
409409
this instanceof IntType and
410410
not this.isExplicitlyUnsigned() and
@@ -420,7 +420,7 @@ class MicrosoftInt32Type extends IntegralType {
420420
*
421421
* This class is meaningless if a Microsoft compiler was not used.
422422
*/
423-
class MicrosoftInt64Type extends IntegralType {
423+
class MicrosoftInt64Type extends BuiltInIntegralType {
424424
MicrosoftInt64Type() {
425425
this instanceof LongLongType and
426426
not this.isExplicitlyUnsigned() and

‎cpp/ql/lib/semmlecode.cpp.dbscheme‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,12 @@ type_operators(
825825
int base_type: @type ref
826826
)
827827

828+
bit_precise_types(
829+
unique int id: @bitpreciseinttype,
830+
boolean is_signed: boolean ref,
831+
int width: int ref
832+
)
833+
828834
case @usertype.kind of
829835
0 = @unknown_usertype
830836
| 1 = @struct
@@ -1197,7 +1203,8 @@ stmtattributes(
11971203
| @routinetype
11981204
| @ptrtomember
11991205
| @decltype
1200-
| @type_operator;
1206+
| @type_operator
1207+
| @bitpreciseinttype;
12011208

12021209
unspecifiedtype(
12031210
unique int type_id: @type ref,

0 commit comments

Comments
 (0)