Unverified Commit 82425768 authored by Oleksandr Tarasiuk's avatar Oleksandr Tarasiuk Committed by GitHub
Browse files

[Clang] prevent crash in delayed default-argument lambda captures (#176749)

Fixes #176534

---

This patch resolves a crash when parsing delayed default arguments that
contain lambda expressions.

```cpp
struct S {
  void f(int x, int = sizeof([x] { return x; }()));
};
```

When late-parsing default arguments that contain lambdas, `Sema` builds
a `FunctionScopes` stack containing only the lambda scope
(`FunctionScopes.size()` equals 1), however, `tryCaptureVariable`
expects an enclosing function scope outside the lambda scope


https://github.com/llvm/llvm-project/blob/41e231cae38028473dd327e8de5f65792b521ffe/clang/lib/Sema/SemaExpr.cpp#L19473-L19474


https://github.com/llvm/llvm-project/blob/41e231cae38028473dd327e8de5f65792b521ffe/clang/lib/Sema/SemaExpr.cpp#L19518

Scope capture handling logic decrements `FunctionScopesIndex` to align
declaration contexts


https://github.com/llvm/llvm-project/blob/41e231cae38028473dd327e8de5f65792b521ffe/clang/lib/Sema/SemaExpr.cpp#L19696

and later relies on it when traversing and accessing outer scopes


https://github.com/llvm/llvm-project/blob/41e231cae38028473dd327e8de5f65792b521ffe/clang/lib/Sema/SemaExpr.cpp#L19522-L19524

preserving the function scope in the late-parsing path prevents invalid
traversal during lambda capture handling
parent 5f0f269d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -506,6 +506,7 @@ Bug Fixes in This Version
- Fixed a crash where constexpr evaluation encountered invalid overrides. (#GH183290)
- Fixed a crash when assigning to an element of an ``ext_vector_type`` with ``bool`` element type. (#GH189260)
- Clang now emits an error for friend declarations of lambda members. (#GH26540)
- Fixed a crash caused by lambda capture handling in delayed default arguments. (#GH176534)

Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+14 −9
Original line number Diff line number Diff line
@@ -381,6 +381,20 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) {
  InFunctionTemplateScope.Scopes.Enter(Scope::FunctionPrototypeScope |
                                       Scope::FunctionDeclarationScope |
                                       Scope::DeclScope);

  // Delayed default arguments or exception specifications may contain lambdas,
  // struct S {
  //   void ICE(int x, int = sizeof([x] { return x; }()));
  // }
  //
  // struct X {
  //   void ICE(int val) noexcept(noexcept([val]{}));
  // };
  // Lambda capture handling in tryCaptureVariable() expects an enclosing
  // function scope in Sema's FunctionScopes stack.
  Sema::FunctionScopeRAII PopFnContext(Actions);
  Actions.PushFunctionScope();

  for (unsigned I = 0, N = LM.DefaultArgs.size(); I != N; ++I) {
    auto Param = cast<ParmVarDecl>(LM.DefaultArgs[I].Param);
    // Introduce the parameter into scope.
@@ -502,20 +516,11 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) {
      FunctionToPush = cast<FunctionDecl>(LM.Method);
    Method = dyn_cast<CXXMethodDecl>(FunctionToPush);

    // Push a function scope so that tryCaptureVariable() can properly visit
    // function scopes involving function parameters that are referenced inside
    // the noexcept specifier e.g. through a lambda expression.
    // Example:
    // struct X {
    //   void ICE(int val) noexcept(noexcept([val]{}));
    // };
    // Setup the CurScope to match the function DeclContext - we have such
    // assumption in IsInFnTryBlockHandler().
    ParseScope FnScope(this, Scope::FnScope);
    Sema::ContextRAII FnContext(Actions, FunctionToPush,
                                /*NewThisContext=*/false);
    Sema::FunctionScopeRAII PopFnContext(Actions);
    Actions.PushFunctionScope();

    Sema::CXXThisScopeRAII ThisScope(
        Actions, Method ? Method->getParent() : nullptr,
+30 −0
Original line number Diff line number Diff line
@@ -288,3 +288,33 @@ auto t() {
  return [](auto w = [&] { return x; }) { }; // expected-error {{lambda expression in default argument cannot capture any entity}}
};
}

namespace GH176534 {
struct S {
  // expected-error@+9 {{reference to local variable 'x' declared in enclosing function 'GH176534::S::a'}}
  // expected-error@+8 {{variable 'x' cannot be implicitly captured in a lambda with no capture-default specified}}
  // expected-note@+7 {{'x' declared here}}
  // expected-note@+6 {{'x' declared here}}
  // expected-note@+5 {{lambda expression begins here}}
  // expected-note@+4 {{capture 'x' by value}}
  // expected-note@+3 {{capture 'x' by reference}}
  // expected-note@+2 {{default capture by value}}
  // expected-note@+1 {{default capture by reference}}
  void a(int x, int = sizeof([x] { return x; }()));

  // expected-error@+8 {{'this' cannot be captured in this context}}
  // expected-error@+7 {{variable 'x' cannot be implicitly captured in a lambda with no capture-default specified}}
  // expected-note@+6 {{default capture by reference}}
  // expected-note@+5 {{lambda expression begins here}}
  // expected-note@+4 {{capture 'x' by value}}
  // expected-note@+3 {{capture 'x' by reference}}
  // expected-note@+2 {{'x' declared here}}
  // expected-note@+1 {{default capture by value}}
  void b(int x, int = sizeof([this] { return x; }));

  // expected-error@+3 {{non-local lambda expression cannot have a capture-default}}
  // expected-error@+2 {{reference to local variable 'x' declared in enclosing function 'GH176534::S::c'}}
  // expected-note@+1 {{'x' declared here}}
  void c(int x, int = sizeof([=] { return x; }));
};
}