@@ -417,56 +417,10 @@ class BaseCallVariable extends AbstractBaseSourceVariable, TBaseCallVariable {
417
417
override CppType getLanguageType ( ) { result = getResultLanguageType ( call ) }
418
418
}
419
419
420
- /**
421
- * Holds if the value pointed to by `operand` can potentially be
422
- * modified be the caller.
423
- */
424
- predicate isModifiableByCall ( ArgumentOperand operand , int indirectionIndex ) {
425
- exists ( CallInstruction call , int index , CppType type |
426
- indirectionIndex = [ 1 .. countIndirectionsForCppType ( type ) ] and
427
- type = getLanguageType ( operand ) and
428
- call .getArgumentOperand ( index ) = operand and
429
- if index = - 1
430
- then
431
- // A qualifier is "modifiable" if:
432
- // 1. the member function is not const specified, or
433
- // 2. the member function is `const` specified, but returns a pointer or reference
434
- // type that is non-const.
435
- //
436
- // To see why this is necessary, consider the following function:
437
- // ```
438
- // struct C {
439
- // void* data_;
440
- // void* data() const { return data; }
441
- // };
442
- // ...
443
- // C c;
444
- // memcpy(c.data(), source, 16)
445
- // ```
446
- // the data pointed to by `c.data_` is potentially modified by the call to `memcpy` even though
447
- // `C::data` has a const specifier. So we further place the restriction that the type returned
448
- // by `call` should not be of the form `const T*` (for some deeply const type `T`).
449
- if call .getStaticCallTarget ( ) instanceof Cpp:: ConstMemberFunction
450
- then
451
- exists ( PointerOrArrayOrReferenceType resultType |
452
- resultType = call .getResultType ( ) and
453
- not resultType .isDeeplyConstBelow ( )
454
- )
455
- else any ( )
456
- else
457
- // An argument is modifiable if it's a non-const pointer or reference type.
458
- isModifiableAt ( type , indirectionIndex )
459
- )
460
- }
461
-
462
- /**
463
- * Holds if `t` is a pointer or reference type that supports at least `indirectionIndex` number
464
- * of indirections, and the `indirectionIndex` indirection cannot be modfiied by passing a
465
- * value of `t` to a function.
466
- */
467
- private predicate isModifiableAtImpl ( CppType cppType , int indirectionIndex ) {
468
- indirectionIndex = [ 1 .. countIndirectionsForCppType ( cppType ) ] and
469
- (
420
+ private module IsModifiableAtImpl {
421
+ bindingset [ cppType, indirectionIndex]
422
+ pragma [ inline_late]
423
+ private predicate impl ( CppType cppType , int indirectionIndex ) {
470
424
exists ( Type pointerType , Type base , Type t |
471
425
pointerType = t .getUnderlyingType ( ) and
472
426
pointerType = any ( Indirection ind ) .getUnderlyingType ( ) and
@@ -480,29 +434,94 @@ private predicate isModifiableAtImpl(CppType cppType, int indirectionIndex) {
480
434
// one of the members was modified.
481
435
exists ( base .stripType ( ) .( Cpp:: Class ) .getAField ( ) )
482
436
)
437
+ }
438
+
439
+ private predicate isModifiableAtImplAtLeast1 ( CppType cppType , int indirectionIndex ) {
440
+ indirectionIndex = [ 1 .. countIndirectionsForCppType ( cppType ) ] and
441
+ (
442
+ impl ( cppType , indirectionIndex )
443
+ or
444
+ // If the `indirectionIndex`'th dereference of a type can be modified
445
+ // then so can the `indirectionIndex + 1`'th dereference.
446
+ isModifiableAtImplAtLeast1 ( cppType , indirectionIndex - 1 )
447
+ )
448
+ }
449
+
450
+ private predicate isModifiableAtImplAt0 ( CppType cppType ) { impl ( cppType , 0 ) }
451
+
452
+ /**
453
+ * Holds if `t` is a pointer or reference type that supports at least `indirectionIndex` number
454
+ * of indirections, and the `indirectionIndex` indirection cannot be modfiied by passing a
455
+ * value of `t` to a function.
456
+ */
457
+ private predicate isModifiableAtImpl ( CppType cppType , int indirectionIndex ) {
458
+ isModifiableAtImplAtLeast1 ( cppType , indirectionIndex )
483
459
or
484
- // If the `indirectionIndex`'th dereference of a type can be modified
485
- // then so can the `indirectionIndex + 1`'th dereference.
486
- isModifiableAtImpl ( cppType , indirectionIndex - 1 )
487
- )
488
- }
460
+ indirectionIndex = 0 and
461
+ isModifiableAtImplAt0 ( cppType )
462
+ }
489
463
490
- /**
491
- * Holds if `t` is a type with at least `indirectionIndex` number of indirections,
492
- * and the `indirectionIndex` indirection can be modified by passing a value of
493
- * type `t` to a function function.
494
- */
495
- bindingset [ indirectionIndex]
496
- predicate isModifiableAt ( CppType cppType , int indirectionIndex ) {
497
- isModifiableAtImpl ( cppType , indirectionIndex )
498
- or
499
- exists ( PointerWrapper pw , Type t |
500
- cppType .hasType ( t , _) and
501
- t .stripType ( ) = pw and
502
- not pw .pointsToConst ( )
503
- )
464
+ /**
465
+ * Holds if `t` is a type with at least `indirectionIndex` number of indirections,
466
+ * and the `indirectionIndex` indirection can be modified by passing a value of
467
+ * type `t` to a function function.
468
+ */
469
+ bindingset [ indirectionIndex]
470
+ predicate isModifiableAt ( CppType cppType , int indirectionIndex ) {
471
+ isModifiableAtImpl ( cppType , indirectionIndex )
472
+ or
473
+ exists ( PointerWrapper pw , Type t |
474
+ cppType .hasType ( t , _) and
475
+ t .stripType ( ) = pw and
476
+ not pw .pointsToConst ( )
477
+ )
478
+ }
479
+
480
+ /**
481
+ * Holds if the value pointed to by `operand` can potentially be
482
+ * modified be the caller.
483
+ */
484
+ predicate isModifiableByCall ( ArgumentOperand operand , int indirectionIndex ) {
485
+ exists ( CallInstruction call , int index , CppType type |
486
+ indirectionIndex = [ 0 .. countIndirectionsForCppType ( type ) ] and
487
+ type = getLanguageType ( operand ) and
488
+ call .getArgumentOperand ( index ) = operand and
489
+ if index = - 1
490
+ then
491
+ // A qualifier is "modifiable" if:
492
+ // 1. the member function is not const specified, or
493
+ // 2. the member function is `const` specified, but returns a pointer or reference
494
+ // type that is non-const.
495
+ //
496
+ // To see why this is necessary, consider the following function:
497
+ // ```
498
+ // struct C {
499
+ // void* data_;
500
+ // void* data() const { return data; }
501
+ // };
502
+ // ...
503
+ // C c;
504
+ // memcpy(c.data(), source, 16)
505
+ // ```
506
+ // the data pointed to by `c.data_` is potentially modified by the call to `memcpy` even though
507
+ // `C::data` has a const specifier. So we further place the restriction that the type returned
508
+ // by `call` should not be of the form `const T*` (for some deeply const type `T`).
509
+ if call .getStaticCallTarget ( ) instanceof Cpp:: ConstMemberFunction
510
+ then
511
+ exists ( PointerOrArrayOrReferenceType resultType |
512
+ resultType = call .getResultType ( ) and
513
+ not resultType .isDeeplyConstBelow ( )
514
+ )
515
+ else any ( )
516
+ else
517
+ // An argument is modifiable if it's a non-const pointer or reference type.
518
+ isModifiableAt ( type , indirectionIndex )
519
+ )
520
+ }
504
521
}
505
522
523
+ import IsModifiableAtImpl
524
+
506
525
abstract class BaseSourceVariableInstruction extends Instruction {
507
526
/** Gets the base source variable accessed by this instruction. */
508
527
abstract BaseSourceVariable getBaseSourceVariable ( ) ;
0 commit comments