@@ -417,56 +417,10 @@ class BaseCallVariable extends AbstractBaseSourceVariable, TBaseCallVariable {
417417 override CppType getLanguageType ( ) { result = getResultLanguageType ( call ) }
418418}
419419
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 ) {
470424 exists ( Type pointerType , Type base , Type t |
471425 pointerType = t .getUnderlyingType ( ) and
472426 pointerType = any ( Indirection ind ) .getUnderlyingType ( ) and
@@ -480,29 +434,94 @@ private predicate isModifiableAtImpl(CppType cppType, int indirectionIndex) {
480434 // one of the members was modified.
481435 exists ( base .stripType ( ) .( Cpp:: Class ) .getAField ( ) )
482436 )
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 )
483459 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+ }
489463
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+ }
504521}
505522
523+ import IsModifiableAtImpl
524+
506525abstract class BaseSourceVariableInstruction extends Instruction {
507526 /** Gets the base source variable accessed by this instruction. */
508527 abstract BaseSourceVariable getBaseSourceVariable ( ) ;
0 commit comments