vsk created this revision. vsk added reviewers: rsmith, rtrieu, erichkeane.
A lambda's closure is initialized when the lambda is declared. For implicit captures, the initialization code emitted from EmitLambdaExpr references source locations *within the lambda body* in the function containing the lambda. This results in a poor debugging experience: we step to the line containing the lambda, then into lambda, out again, over and over, until every capture's field is initialized. To improve stepping behavior, assign an empty location to expressions which initialize an implicit capture within a closure. This prevents the debugger from stepping into a lambda when single-stepping in its parent function. rdar://39807527 https://reviews.llvm.org/D50927 Files: clang/include/clang/AST/Expr.h clang/lib/AST/Expr.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaLambda.cpp clang/test/CodeGenCXX/debug-info-lambda.cpp clang/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp
Index: clang/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp =================================================================== --- clang/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp +++ clang/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp @@ -75,11 +75,14 @@ TEST(RecursiveASTVisitor, VisitsImplicitLambdaCaptureInit) { DeclRefExprVisitor Visitor; Visitor.setShouldVisitImplicitCode(true); - // We're expecting the "i" in the lambda to be visited twice: - // - Once for the DeclRefExpr in the lambda capture initialization (whose - // source code location is set to the first use of the variable). - // - Once for the DeclRefExpr for the use of "i" inside the lambda. - Visitor.ExpectMatch("i", 1, 24, /*Times=*/2); + // We're expecting the "i" in the lambda to be visited just once (for the + // DeclRefExpr for the use of "i" inside the lambda). + // + // Previously, the DeclRefExpr in the implicit lambda capture initialization + // (whose source code location is set to the first use of the variable) was + // also matched. This behavior was removed because it resulted in poor debug + // info. + Visitor.ExpectMatch("i", 1, 24, /*Times=*/1); EXPECT_TRUE(Visitor.runOver( "void f() { int i; [=]{ i; }; }", DeclRefExprVisitor::Lang_CXX11)); Index: clang/test/CodeGenCXX/debug-info-lambda.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/debug-info-lambda.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm \ +// RUN: -debug-info-kind=line-tables-only -std=c++11 %s -o - | FileCheck %s + +// CHECK-LABEL: define{{.*}}lambda_in_func +void lambda_in_func(int &ref) { + // CHECK: [[ref_slot:%.*]] = getelementptr inbounds %class.anon, %class.anon* {{.*}}, i32 0, i32 0, !dbg [[lambda_decl_loc:![0-9]+]] + // CHECK-NEXT: %1 = load i32*, i32** %ref.addr, align 8, !dbg [[init_sequence_loc:![0-9]+]] + // CHECK-NEXT: store i32* %1, i32** %0, align 8, !dbg [[lambda_decl_loc]] + // CHECK-NEXT: call void {{.*}}, !dbg [[lambda_call_loc:![0-9]+]] + + auto helper = [&]() { // CHECK: [[lambda_decl_loc]] = !DILocation(line: [[@LINE]] + ++ref; // CHECK: [[init_sequence_loc]] = !DILocation(line: 0 + }; + helper(); // CHECK: [[lambda_call_loc]] = !DILocation(line: [[@LINE]] +} Index: clang/lib/Sema/SemaLambda.cpp =================================================================== --- clang/lib/Sema/SemaLambda.cpp +++ clang/lib/Sema/SemaLambda.cpp @@ -1392,13 +1392,13 @@ Class->addDecl(Conversion); } -static ExprResult performLambdaVarCaptureInitialization(Sema &S, - const Capture &Capture, - FieldDecl *Field) { +static ExprResult performLambdaVarCaptureInitialization( + Sema &S, const Capture &Capture, FieldDecl *Field, bool IsImplicitCapture) { assert(Capture.isVariableCapture() && "not a variable capture"); auto *Var = Capture.getVariable(); SourceLocation Loc = Capture.getLocation(); + SourceLocation InitLoc = IsImplicitCapture ? SourceLocation() : Loc; // C++11 [expr.prim.lambda]p21: // When the lambda-expression is evaluated, the entities that @@ -1413,7 +1413,7 @@ // An entity captured by a lambda-expression is odr-used (3.2) in // the scope containing the lambda-expression. ExprResult RefResult = S.BuildDeclarationNameExpr( - CXXScopeSpec(), DeclarationNameInfo(Var->getDeclName(), Loc), Var); + CXXScopeSpec(), DeclarationNameInfo(Var->getDeclName(), InitLoc), Var); if (RefResult.isInvalid()) return ExprError(); Expr *Ref = RefResult.get(); @@ -1607,8 +1607,8 @@ Var, From.getEllipsisLoc())); Expr *Init = From.getInitExpr(); if (!Init) { - auto InitResult = - performLambdaVarCaptureInitialization(*this, From, *CurField); + auto InitResult = performLambdaVarCaptureInitialization( + *this, From, *CurField, IsImplicit); if (InitResult.isInvalid()) return ExprError(); Init = InitResult.get(); Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -10686,7 +10686,7 @@ return; } - S.DiagRuntimeBehavior(DRE->getBeginLoc(), DRE, + S.DiagRuntimeBehavior(DRE->getBeginOrDeclLoc(), DRE, S.PDiag(diag) << DRE->getDecl() << OrigDecl->getLocation() << DRE->getSourceRange()); Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ clang/lib/AST/Expr.cpp @@ -457,6 +457,12 @@ return getRAngleLoc(); return getNameInfo().getEndLoc(); } +SourceLocation DeclRefExpr::getBeginOrDeclLoc() const { + SourceLocation BeginLoc = getBeginLoc(); + if (BeginLoc.isValid()) + return BeginLoc; + return getDecl()->getLocation(); +} PredefinedExpr::PredefinedExpr(SourceLocation L, QualType FNTy, IdentType IT, StringLiteral *SL) Index: clang/include/clang/AST/Expr.h =================================================================== --- clang/include/clang/AST/Expr.h +++ clang/include/clang/AST/Expr.h @@ -1083,6 +1083,11 @@ } SourceLocation getEndLoc() const LLVM_READONLY; + /// If this declaration reference explicitly appears in the source, return + /// its location. Otherwise, return the location of the declaration it refers + /// to. + SourceLocation getBeginOrDeclLoc() const LLVM_READONLY; + /// Determine whether this declaration reference was preceded by a /// C++ nested-name-specifier, e.g., \c N::foo. bool hasQualifier() const { return DeclRefExprBits.HasQualifier; }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits