@@ -412,66 +412,55 @@ getDependentValuesFromCall(const CountAttributedType *CAT,
412412 return {std::move (Values)};
413413}
414414
415- // Checks if Self and Other are the same member bases. This supports only very
416- // simple forms of member bases.
417- bool isSameMemberBase (const Expr *Self, const Expr *Other) {
418- for (;;) {
419- if (Self == Other)
420- return true ;
421-
422- const auto *SelfICE = dyn_cast<ImplicitCastExpr>(Self);
423- const auto *OtherICE = dyn_cast<ImplicitCastExpr>(Other);
424- if (SelfICE && OtherICE &&
425- SelfICE->getCastKind () == OtherICE->getCastKind () &&
426- (SelfICE->getCastKind () == CK_LValueToRValue ||
427- SelfICE->getCastKind () == CK_UncheckedDerivedToBase)) {
428- Self = SelfICE->getSubExpr ();
429- Other = OtherICE->getSubExpr ();
430- }
431-
432- const auto *SelfDRE = dyn_cast<DeclRefExpr>(Self);
433- const auto *OtherDRE = dyn_cast<DeclRefExpr>(Other);
434- if (SelfDRE && OtherDRE)
435- return SelfDRE->getDecl () == OtherDRE->getDecl ();
436-
437- if (isa<CXXThisExpr>(Self) && isa<CXXThisExpr>(Other)) {
438- // `Self` and `Other` should be evaluated at the same state so `this` must
439- // mean the same thing for both:
440- return true ;
441- }
442-
443- const auto *SelfME = dyn_cast<MemberExpr>(Self);
444- const auto *OtherME = dyn_cast<MemberExpr>(Other);
445- if (!SelfME || !OtherME ||
446- SelfME->getMemberDecl () != OtherME->getMemberDecl ()) {
447- return false ;
448- }
449-
450- Self = SelfME->getBase ();
451- Other = OtherME->getBase ();
452- }
453- }
454-
455415// Impl of `isCompatibleWithCountExpr`. See `isCompatibleWithCountExpr` for
456- // document.
416+ // high-level document.
417+ //
418+ // This visitor compares two expressions in a tiny subset of the language,
419+ // including DRE of VarDecls, constants, binary operators, subscripts,
420+ // dereferences, member accesses, and member function calls.
421+ //
422+ // - For constants, they are literal constants and expressions have
423+ // compile-time constant values.
424+ // - For a supported dereference expression, it can be either of the forms '*e'
425+ // or '*&e', where 'e' is a supported expression.
426+ // - For a subscript expression, it can be either an array subscript or
427+ // overloaded subscript operator.
428+ // - For a member function call, it must be a call to a 'const' (non-static)
429+ // member function with zero argument. This is to ensure side-effect free.
430+ // Other kinds of function calls are not supported, so an expression of the
431+ // form `f(...)` is not supported.
457432struct CompatibleCountExprVisitor
458- : public ConstStmtVisitor<CompatibleCountExprVisitor, bool , const Expr *> {
433+ : public ConstStmtVisitor<CompatibleCountExprVisitor, bool , const Expr *,
434+ bool > {
435+ // The third 'bool' type parameter for each visit method indicates whether a
436+ // parameter has been substituted with its argument expression in the
437+ // expression AST branch being visited. Since the argument expression may
438+ // contain DREs referencing to the same parameter Decl, the analysis may hit
439+ // an infinite loop of not knowing whether the substitution has happened. A
440+ // typical example that could introduce infinite loop without this knowledge
441+ // is shown below.
442+ // ```
443+ // void f(int * __counted_by(n) p, size_t n) {
444+ // f(p, n);
445+ // }
446+ // ```
459447 using BaseVisitor =
460- ConstStmtVisitor<CompatibleCountExprVisitor, bool , const Expr *>;
448+ ConstStmtVisitor<CompatibleCountExprVisitor, bool , const Expr *, bool >;
461449
462450 const Expr *MemberBase;
463451 const DependentValuesTy *DependentValues;
464452 ASTContext &Ctx;
465453
466454 // If `Deref` has the form `*&e`, return `e`; otherwise return nullptr.
467- const Expr *trySimplifyDerefAddressof (const UnaryOperator *Deref) {
455+ const Expr *trySimplifyDerefAddressof (const UnaryOperator *Deref,
456+ bool hasBeenSubstituted) {
468457 const Expr *DerefOperand = Deref->getSubExpr ()->IgnoreParenImpCasts ();
469458
470459 if (const auto *UO = dyn_cast<UnaryOperator>(DerefOperand))
471460 if (UO->getOpcode () == UO_AddrOf)
472461 return UO->getSubExpr ();
473462 if (const auto *DRE = dyn_cast<DeclRefExpr>(DerefOperand)) {
474- if (!DependentValues)
463+ if (!DependentValues || hasBeenSubstituted )
475464 return nullptr ;
476465
477466 auto I = DependentValues->find (DRE->getDecl ());
@@ -489,18 +478,22 @@ struct CompatibleCountExprVisitor
489478 ASTContext &Ctx)
490479 : MemberBase(MemberBase), DependentValues(DependentValues), Ctx(Ctx) {}
491480
492- bool VisitStmt (const Stmt *S, const Expr *E) { return false ; }
481+ bool VisitStmt (const Stmt *S, const Expr *E, bool hasBeenSubstituted) {
482+ return false ;
483+ }
493484
494- bool VisitImplicitCastExpr (const ImplicitCastExpr *SelfICE,
495- const Expr *Other ) {
496- return Visit (SelfICE->getSubExpr (), Other);
485+ bool VisitImplicitCastExpr (const ImplicitCastExpr *SelfICE, const Expr *Other,
486+ bool hasBeenSubstituted ) {
487+ return Visit (SelfICE->getSubExpr (), Other, hasBeenSubstituted );
497488 }
498489
499- bool VisitParenExpr (const ParenExpr *SelfPE, const Expr *Other) {
500- return Visit (SelfPE->getSubExpr (), Other);
490+ bool VisitParenExpr (const ParenExpr *SelfPE, const Expr *Other,
491+ bool hasBeenSubstituted) {
492+ return Visit (SelfPE->getSubExpr (), Other, hasBeenSubstituted);
501493 }
502494
503- bool VisitIntegerLiteral (const IntegerLiteral *SelfIL, const Expr *Other) {
495+ bool VisitIntegerLiteral (const IntegerLiteral *SelfIL, const Expr *Other,
496+ bool hasBeenSubstituted) {
504497 if (const auto *IntLit =
505498 dyn_cast<IntegerLiteral>(Other->IgnoreParenImpCasts ())) {
506499 return SelfIL == IntLit ||
@@ -510,7 +503,8 @@ struct CompatibleCountExprVisitor
510503 }
511504
512505 bool VisitUnaryExprOrTypeTraitExpr (const UnaryExprOrTypeTraitExpr *Self,
513- const Expr *Other) {
506+ const Expr *Other,
507+ bool hasBeenSubstituted) {
514508 // If `Self` is a `sizeof` expression, try to evaluate and compare the two
515509 // expressions as constants:
516510 if (Self->getKind () == UnaryExprOrTypeTrait::UETT_SizeOf) {
@@ -527,13 +521,19 @@ struct CompatibleCountExprVisitor
527521 return false ;
528522 }
529523
530- bool VisitDeclRefExpr (const DeclRefExpr *SelfDRE, const Expr *Other) {
524+ bool VisitCXXThisExpr (const CXXThisExpr *SelfThis, const Expr *Other,
525+ bool hasBeenSubstituted) {
526+ return isa<CXXThisExpr>(Other->IgnoreParenImpCasts ());
527+ }
528+
529+ bool VisitDeclRefExpr (const DeclRefExpr *SelfDRE, const Expr *Other,
530+ bool hasBeenSubstituted) {
531531 const ValueDecl *SelfVD = SelfDRE->getDecl ();
532532
533- if (DependentValues) {
533+ if (DependentValues && !hasBeenSubstituted ) {
534534 const auto It = DependentValues->find (SelfVD);
535535 if (It != DependentValues->end ())
536- return Visit (It->second , Other);
536+ return Visit (It->second , Other, true );
537537 }
538538
539539 const auto *O = Other->IgnoreParenImpCasts ();
@@ -548,49 +548,110 @@ struct CompatibleCountExprVisitor
548548 const auto *OtherME = dyn_cast<MemberExpr>(O);
549549 if (MemberBase && OtherME) {
550550 return OtherME->getMemberDecl () == SelfVD &&
551- isSameMemberBase (OtherME->getBase (), MemberBase);
551+ Visit (OtherME->getBase (), MemberBase, hasBeenSubstituted );
552552 }
553553
554554 return false ;
555555 }
556556
557- bool VisitMemberExpr (const MemberExpr *Self, const Expr *Other) {
557+ bool VisitMemberExpr (const MemberExpr *Self, const Expr *Other,
558+ bool hasBeenSubstituted) {
558559 // Even though we don't support member expression in counted-by, actual
559560 // arguments can be member expressions.
560561 if (Self == Other)
561562 return true ;
562563 if (const auto *DRE = dyn_cast<DeclRefExpr>(Other->IgnoreParenImpCasts ()))
563564 return MemberBase && Self->getMemberDecl () == DRE->getDecl () &&
564- isSameMemberBase (Self->getBase (), MemberBase);
565+ Visit (Self->getBase (), MemberBase, hasBeenSubstituted);
566+ if (const auto *OtherME =
567+ dyn_cast<MemberExpr>(Other->IgnoreParenImpCasts ())) {
568+ return Self->getMemberDecl () == OtherME->getMemberDecl () &&
569+ Visit (Self->getBase (), OtherME->getBase (), hasBeenSubstituted);
570+ }
565571 return false ;
566572 }
567573
568- bool VisitUnaryOperator (const UnaryOperator *SelfUO, const Expr *Other) {
574+ bool VisitUnaryOperator (const UnaryOperator *SelfUO, const Expr *Other,
575+ bool hasBeenSubstituted) {
569576 if (SelfUO->getOpcode () != UO_Deref)
570577 return false ; // We don't support any other unary operator
571578
572579 if (const auto *OtherUO =
573580 dyn_cast<UnaryOperator>(Other->IgnoreParenImpCasts ())) {
574581 if (SelfUO->getOpcode () == OtherUO->getOpcode ())
575- return Visit (SelfUO->getSubExpr (), OtherUO->getSubExpr ());
582+ return Visit (SelfUO->getSubExpr (), OtherUO->getSubExpr (),
583+ hasBeenSubstituted);
576584 }
577585 // If `Other` is not a dereference expression, try to simplify `SelfUO`:
578- if (const auto *SimplifiedSelf = trySimplifyDerefAddressof (SelfUO)) {
579- return Visit (SimplifiedSelf, Other);
586+ if (const auto *SimplifiedSelf =
587+ trySimplifyDerefAddressof (SelfUO, hasBeenSubstituted)) {
588+ return Visit (SimplifiedSelf, Other, hasBeenSubstituted);
580589 }
581590 return false ;
582591 }
583592
584- bool VisitBinaryOperator (const BinaryOperator *SelfBO, const Expr *Other) {
593+ bool VisitBinaryOperator (const BinaryOperator *SelfBO, const Expr *Other,
594+ bool hasBeenSubstituted) {
585595 const auto *OtherBO =
586596 dyn_cast<BinaryOperator>(Other->IgnoreParenImpCasts ());
587597 if (OtherBO && OtherBO->getOpcode () == SelfBO->getOpcode ()) {
588- return Visit (SelfBO->getLHS (), OtherBO->getLHS ()) &&
589- Visit (SelfBO->getRHS (), OtherBO->getRHS ());
598+ return Visit (SelfBO->getLHS (), OtherBO->getLHS (), hasBeenSubstituted ) &&
599+ Visit (SelfBO->getRHS (), OtherBO->getRHS (), hasBeenSubstituted );
590600 }
591601
592602 return false ;
593603 }
604+
605+ // Support any overloaded operator[] so long as it is a const method.
606+ bool VisitCXXOperatorCallExpr (const CXXOperatorCallExpr *SelfOpCall,
607+ const Expr *Other, bool hasBeenSubstituted) {
608+ if (SelfOpCall->getOperator () != OverloadedOperatorKind::OO_Subscript)
609+ return false ;
610+
611+ const auto *MD = dyn_cast<CXXMethodDecl>(SelfOpCall->getCalleeDecl ());
612+
613+ if (!MD || !MD->isConst ())
614+ return false ;
615+ if (const auto *OtherOpCall =
616+ dyn_cast<CXXOperatorCallExpr>(Other->IgnoreParenImpCasts ()))
617+ if (SelfOpCall->getOperator () == OtherOpCall->getOperator ()) {
618+ return Visit (SelfOpCall->getArg (0 ), OtherOpCall->getArg (0 ),
619+ hasBeenSubstituted) &&
620+ Visit (SelfOpCall->getArg (1 ), OtherOpCall->getArg (1 ),
621+ hasBeenSubstituted);
622+ }
623+ return false ;
624+ }
625+
626+ // Support array/pointer subscript. Even though these operators are generally
627+ // considered unsafe, they can be safely used on constant arrays with
628+ // known-safe literal indexes.
629+ bool VisitArraySubscriptExpr (const ArraySubscriptExpr *SelfAS,
630+ const Expr *Other, bool hasBeenSubstituted) {
631+ if (const auto *OtherAS =
632+ dyn_cast<ArraySubscriptExpr>(Other->IgnoreParenImpCasts ()))
633+ return Visit (SelfAS->getLHS (), OtherAS->getLHS (), hasBeenSubstituted) &&
634+ Visit (SelfAS->getRHS (), OtherAS->getRHS (), hasBeenSubstituted);
635+ return false ;
636+ }
637+
638+ // Support non-static member call:
639+ bool VisitCXXMemberCallExpr (const CXXMemberCallExpr *SelfCall,
640+ const Expr *Other, bool hasBeenSubstituted) {
641+ const CXXMethodDecl *MD = SelfCall->getMethodDecl ();
642+
643+ // The callee member function must be a const function with no parameter:
644+ if (MD->isConst () && MD->param_empty ()) {
645+ if (auto *OtherCall =
646+ dyn_cast<CXXMemberCallExpr>(Other->IgnoreParenImpCasts ())) {
647+ return OtherCall->getMethodDecl () == MD &&
648+ Visit (SelfCall->getImplicitObjectArgument (),
649+ OtherCall->getImplicitObjectArgument (),
650+ hasBeenSubstituted);
651+ }
652+ }
653+ return false ;
654+ }
594655};
595656
596657// TL'DR:
@@ -632,7 +693,7 @@ bool isCompatibleWithCountExpr(const Expr *E, const Expr *ExpectedCountExpr,
632693 const DependentValuesTy *DependentValues,
633694 ASTContext &Ctx) {
634695 CompatibleCountExprVisitor Visitor (MemberBase, DependentValues, Ctx);
635- return Visitor.Visit (ExpectedCountExpr, E);
696+ return Visitor.Visit (ExpectedCountExpr, E, /* hasBeenSubstituted */ false );
636697}
637698
638699// Returns if a pair of expressions contain method calls to .data()/.c_str() and
0 commit comments