<div dir="ltr">Thanks for the prompt response!</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Jan 28, 2021 at 12:52 AM Stephen Kelly <<a href="mailto:steveire@gmail.com">steveire@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
  
    
  
  <div>
    <p><br>
    </p>
    <p>Thanks for reporting. Please try <a href="https://reviews.llvm.org/D95573" target="_blank">https://reviews.llvm.org/D95573</a></p>
    <p>Thanks,</p>
    <p>Stephen.<br>
    </p>
    <div>On 27/01/2021 22:58, Alexander
      Kornienko wrote:<br>
    </div>
    <blockquote type="cite">
      
      <div dir="ltr">
        <div dir="ltr">This patch causes practically infinite traversal
          times on code that contains deeply nested lambdas. Please fix
          or revert the commit.
          <div><br>
          </div>
          <div>There's a very simple test case (add more nesting, if
            it's still fast ;):<br>
            <br>
            <font face="monospace">void f() {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                [] {<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
                }();<br>
              }</font></div>
          <div><br>
          </div>
          <div>Three nested lambdas are enough to demonstrate the issue
            by looking at the AST dump. The body of the innermost lambda
            (0x45593fda99a0) is printed 8 times, and it will be
            traversed 8 times as well by AST matchers:</div>
          <div><font face="monospace">`-FunctionDecl 0x45593fda9198
              </tmp/nested-lambdas.cc:1:1, line:8:1> line:1:6 f
              'void ()'<br>
                `-CompoundStmt 0x45593fdce970 <col:10, line:8:1><br>
                  `-ExprWithCleanups 0x45593fdce958 <line:2:3,
              line:7:5> 'void':'void'<br>
                    `-CXXOperatorCallExpr 0x45593fdce928 <line:2:3,
              line:7:5> 'void':'void' '()'<br>
                      |-ImplicitCastExpr 0x45593fdce8b0 <col:4,
              col:5> 'auto (*)() const -> void'
              <FunctionToPointerDecay><br>
                      | `-DeclRefExpr 0x45593fdce890 <col:4,
              col:5> 'auto () const -> void' lvalue CXXMethod
              0x45593fda9490 'operator()' 'auto () const -> void'<br>
                      `-ImplicitCastExpr 0x45593fdce910 <line:2:3,
              line:7:3> 'const (lambda at
              /tmp/nested-lambdas.cc:2:3)' lvalue <NoOp><br>
                        `-MaterializeTemporaryExpr 0x45593fdce8f8
              <line:2:3, line:7:3> '(lambda at
              /tmp/nested-lambdas.cc:2:3)' lvalue<br>
                          `-LambdaExpr 0x45593fdce788 <line:2:3,
              line:7:3> '(lambda at /tmp/nested-lambdas.cc:2:3)'<br>
                            |-CXXRecordDecl 0x45593fda9350
              <line:2:3> col:3 implicit class definition<br>
                            | |-DefinitionData lambda pass_in_registers
              empty standard_layout trivially_copyable literal
              can_const_default_init<br>
                            | | |-DefaultConstructor
              defaulted_is_constexpr<br>
                            | | |-CopyConstructor simple trivial
              has_const_param needs_implicit implicit_has_const_param<br>
                            | | |-MoveConstructor exists simple trivial
              needs_implicit<br>
                            | | |-CopyAssignment trivial has_const_param
              needs_implicit implicit_has_const_param<br>
                            | | |-MoveAssignment<br>
                            | | `-Destructor simple irrelevant trivial<br>
                            | |-CXXMethodDecl 0x45593fda9490 <col:4,
              line:7:3> line:2:3 used constexpr operator() 'auto ()
              const -> void' inline<br>
                            | | `-CompoundStmt 0x45593fdce4e0 <col:6,
              line:7:3><br>
                            | |   `-ExprWithCleanups 0x45593fdce4c8
              <line:3:3, line:6:5> 'void':'void'<br>
                            | |     `-CXXOperatorCallExpr 0x45593fdce498
              <line:3:3, line:6:5> 'void':'void' '()'<br>
                            | |       |-ImplicitCastExpr 0x45593fdce420
              <col:4, col:5> 'auto (*)() const -> void'
              <FunctionToPointerDecay><br>
                            | |       | `-DeclRefExpr 0x45593fdce400
              <col:4, col:5> 'auto () const -> void' lvalue
              CXXMethod 0x45593fda96c0 'operator()' 'auto () const ->
              void'<br>
                            | |       `-ImplicitCastExpr 0x45593fdce480
              <line:3:3, line:6:3> 'const (lambda at
              /tmp/nested-lambdas.cc:3:3)' lvalue <NoOp><br>
                            | |         `-MaterializeTemporaryExpr
              0x45593fdce468 <line:3:3, line:6:3> '(lambda at
              /tmp/nested-lambdas.cc:3:3)' lvalue<br>
                            | |           `-LambdaExpr 0x45593fdce2f0
              <line:3:3, line:6:3> '(lambda at
              /tmp/nested-lambdas.cc:3:3)'<br>
                            | |             |-CXXRecordDecl
              0x45593fda9588 <line:3:3> col:3 implicit class
              definition<br>
                            | |             | |-DefinitionData lambda
              pass_in_registers empty standard_layout trivially_copyable
              literal can_const_default_init<br>
                            | |             | | |-DefaultConstructor
              defaulted_is_constexpr<br>
                            | |             | | |-CopyConstructor simple
              trivial has_const_param needs_implicit
              implicit_has_const_param<br>
                            | |             | | |-MoveConstructor exists
              simple trivial needs_implicit<br>
                            | |             | | |-CopyAssignment trivial
              has_const_param needs_implicit implicit_has_const_param<br>
                            | |             | | |-MoveAssignment<br>
                            | |             | | `-Destructor simple
              irrelevant trivial<br>
                            | |             | |-CXXMethodDecl
              0x45593fda96c0 <col:4, line:6:3> line:3:3 used
              constexpr operator() 'auto () const -> void' inline<br>
                            | |             | | `-CompoundStmt
              0x45593fdce048 <col:6, line:6:3><br>
                            | |             | |   `-ExprWithCleanups
              0x45593fdce030 <line:4:3, line:5:5> 'void':'void'<br>
                            | |             | |    
              `-CXXOperatorCallExpr 0x45593fdce000 <line:4:3,
              line:5:5> 'void':'void' '()'<br>
                            | |             | |       |-ImplicitCastExpr
              0x45593fda9f88 <col:4, col:5> 'auto (*)() const
              -> void' <FunctionToPointerDecay><br>
                            | |             | |       | `-DeclRefExpr
              0x45593fda9f08 <col:4, col:5> 'auto () const ->
              void' lvalue CXXMethod 0x45593fda98f0 'operator()' 'auto
              () const -> void'<br>
                            | |             | |       `-ImplicitCastExpr
              0x45593fda9fe0 <line:4:3, line:5:3> 'const (lambda
              at /tmp/nested-lambdas.cc:4:3)' lvalue <NoOp><br>
                            | |             | |        
              `-MaterializeTemporaryExpr 0x45593fda9fc8 <line:4:3,
              line:5:3> '(lambda at /tmp/nested-lambdas.cc:4:3)'
              lvalue<br>
                            | |             | |           `-LambdaExpr
              0x45593fda9dd0 <line:4:3, line:5:3> '(lambda at
              /tmp/nested-lambdas.cc:4:3)'<br>
                            | |             | |            
              |-CXXRecordDecl 0x45593fda97b8 <line:4:3> col:3
              implicit class definition<br>
                            | |             | |             |
              |-DefinitionData lambda pass_in_registers empty
              standard_layout trivially_copyable literal
              can_const_default_init<br>
                            | |             | |             | |
              |-DefaultConstructor defaulted_is_constexpr<br>
                            | |             | |             | |
              |-CopyConstructor simple trivial has_const_param
              needs_implicit implicit_has_const_param<br>
                            | |             | |             | |
              |-MoveConstructor exists simple trivial needs_implicit<br>
                            | |             | |             | |
              |-CopyAssignment trivial has_const_param needs_implicit
              implicit_has_const_param<br>
                            | |             | |             | |
              |-MoveAssignment<br>
                            | |             | |             | |
              `-Destructor simple irrelevant trivial<br>
                            | |             | |             |
              |-CXXMethodDecl 0x45593fda98f0 <col:4, line:5:3>
              line:4:3 used constexpr operator() 'auto () const ->
              void' inline<br>
                            | |             | |             | |
              `-CompoundStmt 0x45593fda99a0 <col:6, line:5:3><br>
                            | |             | |             |
              |-CXXConversionDecl 0x45593fda9c68 <line:4:3,
              line:5:3> line:4:3 implicit constexpr operator void
              (*)() 'auto (*() const noexcept)() -> void' inline<br>
                            | |             | |             |
              |-CXXMethodDecl 0x45593fda9d18 <col:3, line:5:3>
              line:4:3 implicit __invoke 'auto () -> void' static
              inline<br>
                            | |             | |             |
              `-CXXDestructorDecl 0x45593fda9df8 <col:3> col:3
              implicit referenced ~ 'void () noexcept' inline default
              trivial<br>
                            | |             | |            
              `-CompoundStmt 0x45593fda99a0 <col:6, line:5:3><br>
                            | |             | |-CXXConversionDecl
              0x45593fdce188 <line:3:3, line:6:3> line:3:3
              implicit constexpr operator void (*)() 'auto (*() const
              noexcept)() -> void' inline<br>
                            | |             | |-CXXMethodDecl
              0x45593fdce238 <col:3, line:6:3> line:3:3 implicit
              __invoke 'auto () -> void' static inline<br>
                            | |             | `-CXXDestructorDecl
              0x45593fdce318 <col:3> col:3 implicit referenced ~
              'void () noexcept' inline default trivial<br>
                            | |             `-CompoundStmt
              0x45593fdce048 <col:6, line:6:3><br>
                            | |               `-ExprWithCleanups
              0x45593fdce030 <line:4:3, line:5:5> 'void':'void'<br>
                            | |                 `-CXXOperatorCallExpr
              0x45593fdce000 <line:4:3, line:5:5> 'void':'void'
              '()'<br>
                            | |                   |-ImplicitCastExpr
              0x45593fda9f88 <col:4, col:5> 'auto (*)() const
              -> void' <FunctionToPointerDecay><br>
                            | |                   | `-DeclRefExpr
              0x45593fda9f08 <col:4, col:5> 'auto () const ->
              void' lvalue CXXMethod 0x45593fda98f0 'operator()' 'auto
              () const -> void'<br>
                            | |                   `-ImplicitCastExpr
              0x45593fda9fe0 <line:4:3, line:5:3> 'const (lambda
              at /tmp/nested-lambdas.cc:4:3)' lvalue <NoOp><br>
                            | |                    
              `-MaterializeTemporaryExpr 0x45593fda9fc8 <line:4:3,
              line:5:3> '(lambda at /tmp/nested-lambdas.cc:4:3)'
              lvalue<br>
                            | |                       `-LambdaExpr
              0x45593fda9dd0 <line:4:3, line:5:3> '(lambda at
              /tmp/nested-lambdas.cc:4:3)'<br>
                            | |                         |-CXXRecordDecl
              0x45593fda97b8 <line:4:3> col:3 implicit class
              definition<br>
                            | |                         |
              |-DefinitionData lambda pass_in_registers empty
              standard_layout trivially_copyable literal
              can_const_default_init<br>
                            | |                         | |
              |-DefaultConstructor defaulted_is_constexpr<br>
                            | |                         | |
              |-CopyConstructor simple trivial has_const_param
              needs_implicit implicit_has_const_param<br>
                            | |                         | |
              |-MoveConstructor exists simple trivial needs_implicit<br>
                            | |                         | |
              |-CopyAssignment trivial has_const_param needs_implicit
              implicit_has_const_param<br>
                            | |                         | |
              |-MoveAssignment<br>
                            | |                         | | `-Destructor
              simple irrelevant trivial<br>
                            | |                         |
              |-CXXMethodDecl 0x45593fda98f0 <col:4, line:5:3>
              line:4:3 used constexpr operator() 'auto () const ->
              void' inline<br>
                            | |                         | |
              `-CompoundStmt 0x45593fda99a0 <col:6, line:5:3><br>
                            | |                         |
              |-CXXConversionDecl 0x45593fda9c68 <line:4:3,
              line:5:3> line:4:3 implicit constexpr operator void
              (*)() 'auto (*() const noexcept)() -> void' inline<br>
                            | |                         |
              |-CXXMethodDecl 0x45593fda9d18 <col:3, line:5:3>
              line:4:3 implicit __invoke 'auto () -> void' static
              inline<br>
                            | |                         |
              `-CXXDestructorDecl 0x45593fda9df8 <col:3> col:3
              implicit referenced ~ 'void () noexcept' inline default
              trivial<br>
                            | |                         `-CompoundStmt
              0x45593fda99a0 <col:6, line:5:3><br>
                            | |-CXXConversionDecl 0x45593fdce620
              <line:2:3, line:7:3> line:2:3 implicit constexpr
              operator void (*)() 'auto (*() const noexcept)() ->
              void' inline<br>
                            | |-CXXMethodDecl 0x45593fdce6d0 <col:3,
              line:7:3> line:2:3 implicit __invoke 'auto () ->
              void' static inline<br>
                            | `-CXXDestructorDecl 0x45593fdce7b0
              <col:3> col:3 implicit referenced ~ 'void ()
              noexcept' inline default trivial<br>
                            `-CompoundStmt 0x45593fdce4e0 <col:6,
              line:7:3><br>
                              `-ExprWithCleanups 0x45593fdce4c8
              <line:3:3, line:6:5> 'void':'void'<br>
                                `-CXXOperatorCallExpr 0x45593fdce498
              <line:3:3, line:6:5> 'void':'void' '()'<br>
                                  |-ImplicitCastExpr 0x45593fdce420
              <col:4, col:5> 'auto (*)() const -> void'
              <FunctionToPointerDecay><br>
                                  | `-DeclRefExpr 0x45593fdce400
              <col:4, col:5> 'auto () const -> void' lvalue
              CXXMethod 0x45593fda96c0 'operator()' 'auto () const ->
              void'<br>
                                  `-ImplicitCastExpr 0x45593fdce480
              <line:3:3, line:6:3> 'const (lambda at
              /tmp/nested-lambdas.cc:3:3)' lvalue <NoOp><br>
                                    `-MaterializeTemporaryExpr
              0x45593fdce468 <line:3:3, line:6:3> '(lambda at
              /tmp/nested-lambdas.cc:3:3)' lvalue<br>
                                      `-LambdaExpr 0x45593fdce2f0
              <line:3:3, line:6:3> '(lambda at
              /tmp/nested-lambdas.cc:3:3)'<br>
                                        |-CXXRecordDecl 0x45593fda9588
              <line:3:3> col:3 implicit class definition<br>
                                        | |-DefinitionData lambda
              pass_in_registers empty standard_layout trivially_copyable
              literal can_const_default_init<br>
                                        | | |-DefaultConstructor
              defaulted_is_constexpr<br>
                                        | | |-CopyConstructor simple
              trivial has_const_param needs_implicit
              implicit_has_const_param<br>
                                        | | |-MoveConstructor exists
              simple trivial needs_implicit<br>
                                        | | |-CopyAssignment trivial
              has_const_param needs_implicit implicit_has_const_param<br>
                                        | | |-MoveAssignment<br>
                                        | | `-Destructor simple
              irrelevant trivial<br>
                                        | |-CXXMethodDecl 0x45593fda96c0
              <col:4, line:6:3> line:3:3 used constexpr operator()
              'auto () const -> void' inline<br>
                                        | | `-CompoundStmt
              0x45593fdce048 <col:6, line:6:3><br>
                                        | |   `-ExprWithCleanups
              0x45593fdce030 <line:4:3, line:5:5> 'void':'void'<br>
                                        | |     `-CXXOperatorCallExpr
              0x45593fdce000 <line:4:3, line:5:5> 'void':'void'
              '()'<br>
                                        | |       |-ImplicitCastExpr
              0x45593fda9f88 <col:4, col:5> 'auto (*)() const
              -> void' <FunctionToPointerDecay><br>
                                        | |       | `-DeclRefExpr
              0x45593fda9f08 <col:4, col:5> 'auto () const ->
              void' lvalue CXXMethod 0x45593fda98f0 'operator()' 'auto
              () const -> void'<br>
                                        | |       `-ImplicitCastExpr
              0x45593fda9fe0 <line:4:3, line:5:3> 'const (lambda
              at /tmp/nested-lambdas.cc:4:3)' lvalue <NoOp><br>
                                        | |        
              `-MaterializeTemporaryExpr 0x45593fda9fc8 <line:4:3,
              line:5:3> '(lambda at /tmp/nested-lambdas.cc:4:3)'
              lvalue<br>
                                        | |           `-LambdaExpr
              0x45593fda9dd0 <line:4:3, line:5:3> '(lambda at
              /tmp/nested-lambdas.cc:4:3)'<br>
                                        | |             |-CXXRecordDecl
              0x45593fda97b8 <line:4:3> col:3 implicit class
              definition<br>
                                        | |             |
              |-DefinitionData lambda pass_in_registers empty
              standard_layout trivially_copyable literal
              can_const_default_init<br>
                                        | |             | |
              |-DefaultConstructor defaulted_is_constexpr<br>
                                        | |             | |
              |-CopyConstructor simple trivial has_const_param
              needs_implicit implicit_has_const_param<br>
                                        | |             | |
              |-MoveConstructor exists simple trivial needs_implicit<br>
                                        | |             | |
              |-CopyAssignment trivial has_const_param needs_implicit
              implicit_has_const_param<br>
                                        | |             | |
              |-MoveAssignment<br>
                                        | |             | | `-Destructor
              simple irrelevant trivial<br>
                                        | |             |
              |-CXXMethodDecl 0x45593fda98f0 <col:4, line:5:3>
              line:4:3 used constexpr operator() 'auto () const ->
              void' inline<br>
                                        | |             | |
              `-CompoundStmt 0x45593fda99a0 <col:6, line:5:3><br>
                                        | |             |
              |-CXXConversionDecl 0x45593fda9c68 <line:4:3,
              line:5:3> line:4:3 implicit constexpr operator void
              (*)() 'auto (*() const noexcept)() -> void' inline<br>
                                        | |             |
              |-CXXMethodDecl 0x45593fda9d18 <col:3, line:5:3>
              line:4:3 implicit __invoke 'auto () -> void' static
              inline<br>
                                        | |             |
              `-CXXDestructorDecl 0x45593fda9df8 <col:3> col:3
              implicit referenced ~ 'void () noexcept' inline default
              trivial<br>
                                        | |             `-CompoundStmt
              0x45593fda99a0 <col:6, line:5:3><br>
                                        | |-CXXConversionDecl
              0x45593fdce188 <line:3:3, line:6:3> line:3:3
              implicit constexpr operator void (*)() 'auto (*() const
              noexcept)() -> void' inline<br>
                                        | |-CXXMethodDecl 0x45593fdce238
              <col:3, line:6:3> line:3:3 implicit __invoke 'auto
              () -> void' static inline<br>
                                        | `-CXXDestructorDecl
              0x45593fdce318 <col:3> col:3 implicit referenced ~
              'void () noexcept' inline default trivial<br>
                                        `-CompoundStmt 0x45593fdce048
              <col:6, line:6:3><br>
                                          `-ExprWithCleanups
              0x45593fdce030 <line:4:3, line:5:5> 'void':'void'<br>
                                            `-CXXOperatorCallExpr
              0x45593fdce000 <line:4:3, line:5:5> 'void':'void'
              '()'<br>
                                              |-ImplicitCastExpr
              0x45593fda9f88 <col:4, col:5> 'auto (*)() const
              -> void' <FunctionToPointerDecay><br>
                                              | `-DeclRefExpr
              0x45593fda9f08 <col:4, col:5> 'auto () const ->
              void' lvalue CXXMethod 0x45593fda98f0 'operator()' 'auto
              () const -> void'<br>
                                              `-ImplicitCastExpr
              0x45593fda9fe0 <line:4:3, line:5:3> 'const (lambda
              at /tmp/nested-lambdas.cc:4:3)' lvalue <NoOp><br>
                                               
              `-MaterializeTemporaryExpr 0x45593fda9fc8 <line:4:3,
              line:5:3> '(lambda at /tmp/nested-lambdas.cc:4:3)'
              lvalue<br>
                                                  `-LambdaExpr
              0x45593fda9dd0 <line:4:3, line:5:3> '(lambda at
              /tmp/nested-lambdas.cc:4:3)'<br>
                                                    |-CXXRecordDecl
              0x45593fda97b8 <line:4:3> col:3 implicit class
              definition<br>
                                                    | |-DefinitionData
              lambda pass_in_registers empty standard_layout
              trivially_copyable literal can_const_default_init<br>
                                                    | |
              |-DefaultConstructor defaulted_is_constexpr<br>
                                                    | |
              |-CopyConstructor simple trivial has_const_param
              needs_implicit implicit_has_const_param<br>
                                                    | |
              |-MoveConstructor exists simple trivial needs_implicit<br>
                                                    | | |-CopyAssignment
              trivial has_const_param needs_implicit
              implicit_has_const_param<br>
                                                    | | |-MoveAssignment<br>
                                                    | | `-Destructor
              simple irrelevant trivial<br>
                                                    | |-CXXMethodDecl
              0x45593fda98f0 <col:4, line:5:3> line:4:3 used
              constexpr operator() 'auto () const -> void' inline<br>
                                                    | | `-CompoundStmt
              0x45593fda99a0 <col:6, line:5:3><br>
                                                    |
              |-CXXConversionDecl 0x45593fda9c68 <line:4:3,
              line:5:3> line:4:3 implicit constexpr operator void
              (*)() 'auto (*() const noexcept)() -> void' inline<br>
                                                    | |-CXXMethodDecl
              0x45593fda9d18 <col:3, line:5:3> line:4:3 implicit
              __invoke 'auto () -> void' static inline<br>
                                                    |
              `-CXXDestructorDecl 0x45593fda9df8 <col:3> col:3
              implicit referenced ~ 'void () noexcept' inline default
              trivial<br>
                                                    `-CompoundStmt
              0x45593fda99a0 <col:6, line:5:3></font><br>
            <br>
          </div>
        </div>
        <br>
        <div class="gmail_quote">
          <div dir="ltr" class="gmail_attr">On Tue, Jan 5, 2021 at 3:45
            PM Stephen Kelly via llvm-branch-commits <<a href="mailto:llvm-branch-commits@lists.llvm.org" target="_blank">llvm-branch-commits@lists.llvm.org</a>>
            wrote:<br>
          </div>
          <blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
            Author: Stephen Kelly<br>
            Date: 2021-01-05T14:39:46Z<br>
            New Revision: c3a21e5de3dc3f55e4d219afd55dec518159d356<br>
            <br>
            URL: <a href="https://github.com/llvm/llvm-project/commit/c3a21e5de3dc3f55e4d219afd55dec518159d356" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/c3a21e5de3dc3f55e4d219afd55dec518159d356</a><br>
            DIFF: <a href="https://github.com/llvm/llvm-project/commit/c3a21e5de3dc3f55e4d219afd55dec518159d356.diff" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/c3a21e5de3dc3f55e4d219afd55dec518159d356.diff</a><br>
            <br>
            LOG: [ASTMatchers] Ensure that we can match inside lambdas<br>
            <br>
            Because we don't know in ASTMatchFinder whether we're
            matching in AsIs<br>
            or IgnoreUnlessSpelledInSource mode, we need to traverse the
            lambda<br>
            twice, but store whether we're matching in nodes spelled in
            source or<br>
            not.<br>
            <br>
            Differential Revision: <a href="https://reviews.llvm.org/D93688" rel="noreferrer" target="_blank">https://reviews.llvm.org/D93688</a><br>
            <br>
            Added: <br>
            <br>
            <br>
            Modified: <br>
                clang/include/clang/ASTMatchers/ASTMatchersInternal.h<br>
                clang/lib/ASTMatchers/ASTMatchFinder.cpp<br>
                clang/lib/ASTMatchers/ASTMatchersInternal.cpp<br>
                clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp<br>
            <br>
            Removed: <br>
            <br>
            <br>
            <br>
################################################################################<br>
            diff  --git
            a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
            b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h<br>
            index 46de4093272d..f49728d1f50e 100644<br>
            --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h<br>
            +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h<br>
            @@ -723,6 +723,8 @@ class ASTMatchFinder {<br>
            <br>
               virtual bool IsMatchingInASTNodeNotSpelledInSource()
            const = 0;<br>
            <br>
            +  virtual bool IsMatchingInASTNodeNotAsIs() const = 0;<br>
            +<br>
               bool isTraversalIgnoringImplicitNodes() const;<br>
            <br>
             protected:<br>
            <br>
            diff  --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp
            b/clang/lib/ASTMatchers/ASTMatchFinder.cpp<br>
            index 762885fa0052..af21e2283d8b 100644<br>
            --- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp<br>
            +++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp<br>
            @@ -475,6 +475,55 @@ class MatchASTVisitor : public
            RecursiveASTVisitor<MatchASTVisitor>,<br>
                     }<br>
                   }<br>
                   return true;<br>
            +    } else if (auto *LE = dyn_cast<LambdaExpr>(S)) {<br>
            +      for (auto I : llvm::zip(LE->captures(),
            LE->capture_inits())) {<br>
            +        auto C = std::get<0>(I);<br>
            +        ASTNodeNotSpelledInSourceScope RAII(<br>
            +            this, TraversingASTNodeNotSpelledInSource ||
            !C.isExplicit());<br>
            +        TraverseLambdaCapture(LE, &C,
            std::get<1>(I));<br>
            +      }<br>
            +<br>
            +      {<br>
            +        ASTNodeNotSpelledInSourceScope RAII(this, true);<br>
            +        TraverseDecl(LE->getLambdaClass());<br>
          </blockquote>
          <div><br>
          </div>
          <div>The line above triggers an additional traversal of all
            nested lambdas, leading to exponential time growth.</div>
          <div><br>
          </div>
          <blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
            +      }<br>
            +      {<br>
            +        ASTNodeNotAsIsSourceScope RAII(this, true);<br>
            +<br>
            +        // We need to poke around to find the bits that
            might be explicitly<br>
            +        // written.<br>
            +        TypeLoc TL =
            LE->getCallOperator()->getTypeSourceInfo()->getTypeLoc();<br>
            +        FunctionProtoTypeLoc Proto =
            TL.getAsAdjusted<FunctionProtoTypeLoc>();<br>
            +<br>
            +        if (auto *TPL = LE->getTemplateParameterList())
            {<br>
            +          for (NamedDecl *D : *TPL) {<br>
            +            TraverseDecl(D);<br>
            +          }<br>
            +          if (Expr *RequiresClause =
            TPL->getRequiresClause()) {<br>
            +            TraverseStmt(RequiresClause);<br>
            +          }<br>
            +        }<br>
            +<br>
            +        if (LE->hasExplicitParameters()) {<br>
            +          // Visit parameters.<br>
            +          for (ParmVarDecl *Param : Proto.getParams())<br>
            +            TraverseDecl(Param);<br>
            +        }<br>
            +<br>
            +        const auto *T = Proto.getTypePtr();<br>
            +        for (const auto &E : T->exceptions())<br>
            +          TraverseType(E);<br>
            +<br>
            +        if (Expr *NE = T->getNoexceptExpr())<br>
            +          TraverseStmt(NE, Queue);<br>
            +<br>
            +        if (LE->hasExplicitResultType())<br>
            +          TraverseTypeLoc(Proto.getReturnLoc());<br>
            +        TraverseStmt(LE->getTrailingRequiresClause());<br>
            +<br>
            +        TraverseStmt(LE->getBody());<br>
            +      }<br>
            +      return true;<br>
                 }<br>
                 return
            RecursiveASTVisitor<MatchASTVisitor>::dataTraverseNode(S,
            Queue);<br>
               }<br>
            @@ -617,6 +666,9 @@ class MatchASTVisitor : public
            RecursiveASTVisitor<MatchASTVisitor>,<br>
               bool IsMatchingInASTNodeNotSpelledInSource() const
            override {<br>
                 return TraversingASTNodeNotSpelledInSource;<br>
               }<br>
            +  bool IsMatchingInASTNodeNotAsIs() const override {<br>
            +    return TraversingASTNodeNotAsIs;<br>
            +  }<br>
            <br>
               bool TraverseTemplateInstantiations(ClassTemplateDecl *D)
            {<br>
                 ASTNodeNotSpelledInSourceScope RAII(this, true);<br>
            @@ -638,6 +690,7 @@ class MatchASTVisitor : public
            RecursiveASTVisitor<MatchASTVisitor>,<br>
            <br>
             private:<br>
               bool TraversingASTNodeNotSpelledInSource = false;<br>
            +  bool TraversingASTNodeNotAsIs = false;<br>
               bool TraversingASTChildrenNotSpelledInSource = false;<br>
            <br>
               struct ASTNodeNotSpelledInSourceScope {<br>
            @@ -654,6 +707,18 @@ class MatchASTVisitor : public
            RecursiveASTVisitor<MatchASTVisitor>,<br>
                 bool MB;<br>
               };<br>
            <br>
            +  struct ASTNodeNotAsIsSourceScope {<br>
            +    ASTNodeNotAsIsSourceScope(MatchASTVisitor *V, bool B)<br>
            +        : MV(V), MB(V->TraversingASTNodeNotAsIs) {<br>
            +      V->TraversingASTNodeNotAsIs = B;<br>
            +    }<br>
            +    ~ASTNodeNotAsIsSourceScope() {
            MV->TraversingASTNodeNotAsIs = MB; }<br>
            +<br>
            +  private:<br>
            +    MatchASTVisitor *MV;<br>
            +    bool MB;<br>
            +  };<br>
            +<br>
               struct ASTChildrenNotSpelledInSource {<br>
                 ASTChildrenNotSpelledInSource(MatchASTVisitor *V, bool
            B)<br>
                     : MV(V),
            MB(V->TraversingASTChildrenNotSpelledInSource) {<br>
            <br>
            diff  --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
            b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp<br>
            index 3c19bceb079e..eb0fffcc3c37 100644<br>
            --- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp<br>
            +++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp<br>
            @@ -293,6 +293,10 @@ bool DynTypedMatcher::matches(const
            DynTypedNode &DynNode,<br>
                   Finder->IsMatchingInASTNodeNotSpelledInSource())<br>
                 return false;<br>
            <br>
            +  if (!Finder->isTraversalIgnoringImplicitNodes()
            &&<br>
            +      Finder->IsMatchingInASTNodeNotAsIs())<br>
            +    return false;<br>
            +<br>
               auto N =<br>
                 
 Finder->getASTContext().getParentMapContext().traverseIgnored(DynNode);<br>
            <br>
            @@ -317,6 +321,10 @@ bool
            DynTypedMatcher::matchesNoKindCheck(const DynTypedNode
            &DynNode,<br>
                   Finder->IsMatchingInASTNodeNotSpelledInSource())<br>
                 return false;<br>
            <br>
            +  if (!Finder->isTraversalIgnoringImplicitNodes()
            &&<br>
            +      Finder->IsMatchingInASTNodeNotAsIs())<br>
            +    return false;<br>
            +<br>
               auto N =<br>
                 
 Finder->getASTContext().getParentMapContext().traverseIgnored(DynNode);<br>
            <br>
            <br>
            diff  --git
            a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
            b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp<br>
            index a3a3a911b85c..1dc5179ce857 100644<br>
            ---
            a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp<br>
            +++
            b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp<br>
            @@ -3065,6 +3065,33 @@ void func14() {<br>
                   traverse(TK_IgnoreUnlessSpelledInSource,<br>
                            functionDecl(hasName("func14"),
            hasDescendant(floatLiteral()))),<br>
                   langCxx20OrLater()));<br>
            +<br>
            +  Code = R"cpp(<br>
            +void foo() {<br>
            +    int explicit_captured = 0;<br>
            +    int implicit_captured = 0;<br>
            +    auto l = [&, explicit_captured](int i) {<br>
            +        if (i || explicit_captured || implicit_captured)
            return;<br>
            +    };<br>
            +}<br>
            +)cpp";<br>
            +<br>
            +  EXPECT_TRUE(matches(Code, traverse(TK_AsIs, ifStmt())));<br>
            +  EXPECT_TRUE(<br>
            +      matches(Code,
            traverse(TK_IgnoreUnlessSpelledInSource, ifStmt())));<br>
            +<br>
            +  auto lambdaExplicitCapture = declRefExpr(<br>
            +      to(varDecl(hasName("explicit_captured"))),
            unless(hasAncestor(ifStmt())));<br>
            +  auto lambdaImplicitCapture = declRefExpr(<br>
            +      to(varDecl(hasName("implicit_captured"))),
            unless(hasAncestor(ifStmt())));<br>
            +<br>
            +  EXPECT_TRUE(matches(Code, traverse(TK_AsIs,
            lambdaExplicitCapture)));<br>
            +  EXPECT_TRUE(matches(<br>
            +      Code, traverse(TK_IgnoreUnlessSpelledInSource,
            lambdaExplicitCapture)));<br>
            +<br>
            +  EXPECT_TRUE(matches(Code, traverse(TK_AsIs,
            lambdaImplicitCapture)));<br>
            +  EXPECT_FALSE(matches(<br>
            +      Code, traverse(TK_IgnoreUnlessSpelledInSource,
            lambdaImplicitCapture)));<br>
             }<br>
            <br>
             TEST(IgnoringImpCasts, MatchesImpCasts) {<br>
            <br>
            <br>
            <br>
            _______________________________________________<br>
            llvm-branch-commits mailing list<br>
            <a href="mailto:llvm-branch-commits@lists.llvm.org" target="_blank">llvm-branch-commits@lists.llvm.org</a><br>
            <a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits</a><br>
          </blockquote>
        </div>
      </div>
    </blockquote>
  </div>

</blockquote></div>