[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-06-16 Thread Timm Bäder via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG461f91b1e4ef: [clang][Interp] Handle LambdaExprs (authored 
by tbaeder).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

Files:
  clang/lib/AST/Interp/ByteCodeEmitter.cpp
  clang/lib/AST/Interp/ByteCodeEmitter.h
  clang/lib/AST/Interp/ByteCodeExprGen.cpp
  clang/lib/AST/Interp/ByteCodeExprGen.h
  clang/lib/AST/Interp/EvalEmitter.h
  clang/lib/AST/Interp/Interp.h
  clang/test/AST/Interp/lambda.cpp

Index: clang/test/AST/Interp/lambda.cpp
===
--- /dev/null
+++ clang/test/AST/Interp/lambda.cpp
@@ -0,0 +1,109 @@
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify -std=c++20 %s
+// RUN: %clang_cc1 -verify=ref -std=c++20 %s
+
+constexpr int a = 12;
+constexpr int f = [c = a]() { return c; }();
+static_assert(f == a);
+
+
+constexpr int inc() {
+  int a = 10;
+  auto f = [&a]() {
+++a;
+  };
+
+  f();f();
+
+  return a;
+}
+static_assert(inc() == 12);
+
+constexpr int add(int a, int b) {
+  auto doIt = [a, b](int c) {
+return a + b + c;
+  };
+
+  return doIt(2);
+}
+static_assert(add(4, 5) == 11);
+
+
+constexpr int add2(int a, int b) {
+  auto doIt = [a, b](int c) {
+auto bar = [a]() { return a; };
+auto bar2 = [b]() { return b; };
+
+return bar() + bar2() + c;
+  };
+
+  return doIt(2);
+}
+static_assert(add2(4, 5) == 11);
+
+
+constexpr int div(int a, int b) {
+  auto f = [=]() {
+return a / b; // expected-note {{division by zero}} \
+  // ref-note {{division by zero}}
+  };
+
+  return f(); // expected-note {{in call to '&f->operator()()'}} \
+  // ref-note {{in call to '&f->operator()()'}}
+}
+static_assert(div(8, 2) == 4);
+static_assert(div(8, 0) == 4); // expected-error {{not an integral constant expression}} \
+   // expected-note {{in call to 'div(8, 0)'}} \
+   // ref-error {{not an integral constant expression}} \
+   // ref-note {{in call to 'div(8, 0)'}}
+
+
+struct F {
+  float f;
+};
+
+constexpr float captureStruct() {
+  F someF = {1.0};
+
+  auto p = [someF]() {
+return someF.f;
+  };
+
+  return p();
+}
+
+static_assert(captureStruct() == 1.0);
+
+
+int constexpr FunCase() {
+return [x = 10] {
+   decltype(x) y; // type int b/c not odr use
+  // refers to original init-capture
+   auto &z = x; // type const int & b/c odr use
+ // refers to lambdas copy of x
+y = 10; // Ok
+//z = 10; // Ill-formed
+return y;
+}();
+}
+
+constexpr int WC = FunCase();
+
+
+namespace LambdaParams {
+  template
+  constexpr void callThis(T t) {
+return t();
+  }
+
+  constexpr int foo() {
+int a = 0;
+auto f = [&a]() { ++a; };
+
+callThis(f);
+
+return a;
+  }
+  /// FIXME: This should work in the new interpreter.
+  static_assert(foo() == 1); // expected-error {{not an integral constant expression}}
+}
+
Index: clang/lib/AST/Interp/Interp.h
===
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -840,6 +840,7 @@
   const Pointer &Field = Obj.atField(I);
   if (!CheckStore(S, OpPC, Field))
 return false;
+  Field.initialize();
   Field.deref() = Value;
   return true;
 }
Index: clang/lib/AST/Interp/EvalEmitter.h
===
--- clang/lib/AST/Interp/EvalEmitter.h
+++ clang/lib/AST/Interp/EvalEmitter.h
@@ -76,6 +76,10 @@
 
   /// Parameter indices.
   llvm::DenseMap Params;
+  /// Lambda captures.
+  /// Map from Decl* to [Offset, IsReference] pair.
+  llvm::DenseMap> LambdaCaptures;
+  unsigned LambdaThisCapture;
   /// Local descriptors.
   llvm::SmallVector, 2> Descriptors;
 
Index: clang/lib/AST/Interp/ByteCodeExprGen.h
===
--- clang/lib/AST/Interp/ByteCodeExprGen.h
+++ clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -93,6 +93,7 @@
   bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E);
   bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E);
   bool VisitTypeTraitExpr(const TypeTraitExpr *E);
+  bool VisitLambdaExpr(const LambdaExpr *E);
 
 protected:
   bool visitExpr(const Expr *E) override;
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -895,6 +895,43 @@
   return this->emitConstBool(E->getValue(), E);
 }
 
+template 
+bool ByteCodeExprGen::VisitLambdaExpr(const LambdaExpr *E) {
+  // XXX We assume here that a pointer-to-initialize is on the stack.
+
+  const Record *R = P.getOrCreateRecord(E->getLambdaClass());
+
+  auto *CaptureI

[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-05-16 Thread Aaron Ballman via Phabricator via cfe-commits
aaron.ballman accepted this revision.
aaron.ballman added a comment.
This revision is now accepted and ready to land.

LGTM




Comment at: clang/lib/AST/Interp/ByteCodeExprGen.cpp:954
+bool ByteCodeExprGen::VisitLambdaExpr(const LambdaExpr *E) {
+  // XXX We assume here that a pointer-to-initialize is on the stack.
+




CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-05-15 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder updated this revision to Diff 522204.
tbaeder marked an inline comment as done.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

Files:
  clang/lib/AST/Interp/ByteCodeEmitter.cpp
  clang/lib/AST/Interp/ByteCodeEmitter.h
  clang/lib/AST/Interp/ByteCodeExprGen.cpp
  clang/lib/AST/Interp/ByteCodeExprGen.h
  clang/lib/AST/Interp/EvalEmitter.h
  clang/lib/AST/Interp/Interp.h
  clang/test/AST/Interp/lambda.cpp

Index: clang/test/AST/Interp/lambda.cpp
===
--- /dev/null
+++ clang/test/AST/Interp/lambda.cpp
@@ -0,0 +1,109 @@
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify -std=c++20 %s
+// RUN: %clang_cc1 -verify=ref -std=c++20 %s
+
+constexpr int a = 12;
+constexpr int f = [c = a]() { return c; }();
+static_assert(f == a);
+
+
+constexpr int inc() {
+  int a = 10;
+  auto f = [&a]() {
+++a;
+  };
+
+  f();f();
+
+  return a;
+}
+static_assert(inc() == 12);
+
+constexpr int add(int a, int b) {
+  auto doIt = [a, b](int c) {
+return a + b + c;
+  };
+
+  return doIt(2);
+}
+static_assert(add(4, 5) == 11);
+
+
+constexpr int add2(int a, int b) {
+  auto doIt = [a, b](int c) {
+auto bar = [a]() { return a; };
+auto bar2 = [b]() { return b; };
+
+return bar() + bar2() + c;
+  };
+
+  return doIt(2);
+}
+static_assert(add2(4, 5) == 11);
+
+
+constexpr int div(int a, int b) {
+  auto f = [=]() {
+return a / b; // expected-note {{division by zero}} \
+  // ref-note {{division by zero}}
+  };
+
+  return f(); // expected-note {{in call to '&f->operator()()'}} \
+  // ref-note {{in call to '&f->operator()()'}}
+}
+static_assert(div(8, 2) == 4);
+static_assert(div(8, 0) == 4); // expected-error {{not an integral constant expression}} \
+   // expected-note {{in call to 'div(8, 0)'}} \
+   // ref-error {{not an integral constant expression}} \
+   // ref-note {{in call to 'div(8, 0)'}}
+
+
+struct F {
+  float f;
+};
+
+constexpr float captureStruct() {
+  F someF = {1.0};
+
+  auto p = [someF]() {
+return someF.f;
+  };
+
+  return p();
+}
+
+static_assert(captureStruct() == 1.0);
+
+
+int constexpr FunCase() {
+return [x = 10] {
+   decltype(x) y; // type int b/c not odr use
+  // refers to original init-capture
+   auto &z = x; // type const int & b/c odr use
+ // refers to lambdas copy of x
+y = 10; // Ok
+//z = 10; // Ill-formed
+return y;
+}();
+}
+
+constexpr int WC = FunCase();
+
+
+namespace LambdaParams {
+  template
+  constexpr void callThis(T t) {
+return t();
+  }
+
+  constexpr int foo() {
+int a = 0;
+auto f = [&a]() { ++a; };
+
+callThis(f);
+
+return a;
+  }
+  /// FIXME: This should work in the new interpreter.
+  static_assert(foo() == 1); // expected-error {{not an integral constant expression}}
+}
+
Index: clang/lib/AST/Interp/Interp.h
===
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -850,6 +850,7 @@
   const Pointer &Field = Obj.atField(I);
   if (!CheckStore(S, OpPC, Field))
 return false;
+  Field.initialize();
   Field.deref() = Value;
   return true;
 }
Index: clang/lib/AST/Interp/EvalEmitter.h
===
--- clang/lib/AST/Interp/EvalEmitter.h
+++ clang/lib/AST/Interp/EvalEmitter.h
@@ -76,6 +76,10 @@
 
   /// Parameter indices.
   llvm::DenseMap Params;
+  /// Lambda captures.
+  /// Map from Decl* to [Offset, IsReference] pair.
+  llvm::DenseMap> LambdaCaptures;
+  unsigned LambdaThisCapture;
   /// Local descriptors.
   llvm::SmallVector, 2> Descriptors;
 
Index: clang/lib/AST/Interp/ByteCodeExprGen.h
===
--- clang/lib/AST/Interp/ByteCodeExprGen.h
+++ clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -93,6 +93,7 @@
   bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E);
   bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E);
   bool VisitTypeTraitExpr(const TypeTraitExpr *E);
+  bool VisitLambdaExpr(const LambdaExpr *E);
 
 protected:
   bool visitExpr(const Expr *E) override;
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -949,6 +949,43 @@
   return this->emitConstBool(E->getValue(), E);
 }
 
+template 
+bool ByteCodeExprGen::VisitLambdaExpr(const LambdaExpr *E) {
+  // XXX We assume here that a pointer-to-initialize is on the stack.
+
+  const Record *R = P.getOrCreateRecord(E->getLambdaClass());
+
+  auto *CaptureInitIt = E->capture_init_begin();
+  // Initialize all fields (which represent lambda captures) of the
+  // record wi

[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-05-08 Thread Shafik Yaghmour via Phabricator via cfe-commits
shafik added inline comments.



Comment at: clang/test/AST/Interp/lambda.cpp:5
+constexpr int a = 12;
+constexpr int f = [c = a]() { return c; }();
+static_assert(f == a);

Fun case

```
int constexpr f() {
return [x = 10] {
   decltype(x) y; // type int b/c not odr use
  // refers to original init-capture
   auto &z = x; // type const int & b/c odr use
 // refers to lambdas copy of x
y = 10; // Ok
//z = 10; // Ill-formed
return y;
}();
}

constexpr int x = f();
```


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-05-08 Thread Aaron Ballman via Phabricator via cfe-commits
aaron.ballman added inline comments.



Comment at: clang/test/AST/Interp/lambda.cpp:92
+  static_assert(foo() == 1); // expected-error {{not an integral constant 
expression}}
+}
+

tbaeder wrote:
> tbaeder wrote:
> > tbaeder wrote:
> > > aaron.ballman wrote:
> > > > How about some tests like:
> > > > ```
> > > > constexpr int call_thru_func_ptr(int i) {
> > > >   auto l = [](int i) { return i; };
> > > >   int (*fp)(int) = l;
> > > >   return fp(i);  
> > > > }
> > > > static_assert(call_thru_func_ptr(12) == 12);
> > > > 
> > > > constexpr int call_through_copied_lambda(auto lam, int i) {
> > > >   auto copy = lam;
> > > >   return copy(i);
> > > > }
> > > > 
> > > > constexpr int call_through_copied_lambda(auto lam) {
> > > >   auto copy = lam;
> > > >   return copy();
> > > > }
> > > > 
> > > > void func() {
> > > >   constexpr int i = 12;
> > > >   static_assert(call_through_copied_lambda([i]() { return i; }) == 12);
> > > > }
> > > > ```
> > > Heh:
> > > ```
> > > array.cpp:1245:15: error: static assertion expression is not an integral 
> > > constant expression
> > >  1245 | static_assert(call_thru_func_ptr(12) == 12);
> > >   |   ^~~~
> > > array.cpp:1243:10: note: non-constexpr function '__invoke' cannot be used 
> > > in a constant expression
> > >  1243 |   return fp(i);
> > >   |  ^
> > > array.cpp:1245:15: note: in call to 'call_thru_func_ptr(12)'
> > >  1245 | static_assert(call_thru_func_ptr(12) == 12);
> > >   |   ^
> > > array.cpp:1239:12: note: declared here
> > >  1239 |   auto l = [](int i) { return i; };
> > >   |^
> > > 
> > > ```
> > Ah, I didn't know there is something like a "lambda static invoker". I see 
> > the current interpreter basically checks for that and then calls the lambda 
> > call operator instead. Doing that is hard for me though, because the call 
> > operator requires different arguments and I can't just itnogre the static 
> > invoker either because it has an empty body.
> Okay, I think I figured it out, I'm just special-casing the static invoker 
> and emitting byte code for its body manually. This will make the patch larger 
> though.
Hmmm, aren't you going to need to emit a body for the static invoker for 
calling through a function pointer to it (the `call_thru_func_ptr` example)? I 
would imagine you'd need to emit both the static invoker and the call operator 
and have the static invoker call the call operator.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-05-08 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder added inline comments.



Comment at: clang/test/AST/Interp/lambda.cpp:92
+  static_assert(foo() == 1); // expected-error {{not an integral constant 
expression}}
+}
+

tbaeder wrote:
> tbaeder wrote:
> > aaron.ballman wrote:
> > > How about some tests like:
> > > ```
> > > constexpr int call_thru_func_ptr(int i) {
> > >   auto l = [](int i) { return i; };
> > >   int (*fp)(int) = l;
> > >   return fp(i);  
> > > }
> > > static_assert(call_thru_func_ptr(12) == 12);
> > > 
> > > constexpr int call_through_copied_lambda(auto lam, int i) {
> > >   auto copy = lam;
> > >   return copy(i);
> > > }
> > > 
> > > constexpr int call_through_copied_lambda(auto lam) {
> > >   auto copy = lam;
> > >   return copy();
> > > }
> > > 
> > > void func() {
> > >   constexpr int i = 12;
> > >   static_assert(call_through_copied_lambda([i]() { return i; }) == 12);
> > > }
> > > ```
> > Heh:
> > ```
> > array.cpp:1245:15: error: static assertion expression is not an integral 
> > constant expression
> >  1245 | static_assert(call_thru_func_ptr(12) == 12);
> >   |   ^~~~
> > array.cpp:1243:10: note: non-constexpr function '__invoke' cannot be used 
> > in a constant expression
> >  1243 |   return fp(i);
> >   |  ^
> > array.cpp:1245:15: note: in call to 'call_thru_func_ptr(12)'
> >  1245 | static_assert(call_thru_func_ptr(12) == 12);
> >   |   ^
> > array.cpp:1239:12: note: declared here
> >  1239 |   auto l = [](int i) { return i; };
> >   |^
> > 
> > ```
> Ah, I didn't know there is something like a "lambda static invoker". I see 
> the current interpreter basically checks for that and then calls the lambda 
> call operator instead. Doing that is hard for me though, because the call 
> operator requires different arguments and I can't just itnogre the static 
> invoker either because it has an empty body.
Okay, I think I figured it out, I'm just special-casing the static invoker and 
emitting byte code for its body manually. This will make the patch larger 
though.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-05-07 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder added inline comments.



Comment at: clang/test/AST/Interp/lambda.cpp:92
+  static_assert(foo() == 1); // expected-error {{not an integral constant 
expression}}
+}
+

tbaeder wrote:
> aaron.ballman wrote:
> > How about some tests like:
> > ```
> > constexpr int call_thru_func_ptr(int i) {
> >   auto l = [](int i) { return i; };
> >   int (*fp)(int) = l;
> >   return fp(i);  
> > }
> > static_assert(call_thru_func_ptr(12) == 12);
> > 
> > constexpr int call_through_copied_lambda(auto lam, int i) {
> >   auto copy = lam;
> >   return copy(i);
> > }
> > 
> > constexpr int call_through_copied_lambda(auto lam) {
> >   auto copy = lam;
> >   return copy();
> > }
> > 
> > void func() {
> >   constexpr int i = 12;
> >   static_assert(call_through_copied_lambda([i]() { return i; }) == 12);
> > }
> > ```
> Heh:
> ```
> array.cpp:1245:15: error: static assertion expression is not an integral 
> constant expression
>  1245 | static_assert(call_thru_func_ptr(12) == 12);
>   |   ^~~~
> array.cpp:1243:10: note: non-constexpr function '__invoke' cannot be used in 
> a constant expression
>  1243 |   return fp(i);
>   |  ^
> array.cpp:1245:15: note: in call to 'call_thru_func_ptr(12)'
>  1245 | static_assert(call_thru_func_ptr(12) == 12);
>   |   ^
> array.cpp:1239:12: note: declared here
>  1239 |   auto l = [](int i) { return i; };
>   |^
> 
> ```
Ah, I didn't know there is something like a "lambda static invoker". I see the 
current interpreter basically checks for that and then calls the lambda call 
operator instead. Doing that is hard for me though, because the call operator 
requires different arguments and I can't just itnogre the static invoker either 
because it has an empty body.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-05-07 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder updated this revision to Diff 520155.
tbaeder marked 3 inline comments as done.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

Files:
  clang/lib/AST/Interp/ByteCodeEmitter.cpp
  clang/lib/AST/Interp/ByteCodeEmitter.h
  clang/lib/AST/Interp/ByteCodeExprGen.cpp
  clang/lib/AST/Interp/ByteCodeExprGen.h
  clang/lib/AST/Interp/EvalEmitter.h
  clang/lib/AST/Interp/Interp.h
  clang/test/AST/Interp/lambda.cpp

Index: clang/test/AST/Interp/lambda.cpp
===
--- /dev/null
+++ clang/test/AST/Interp/lambda.cpp
@@ -0,0 +1,93 @@
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify -std=c++20 %s
+// RUN: %clang_cc1 -verify=ref -std=c++20 %s
+
+constexpr int a = 12;
+constexpr int f = [c = a]() { return c; }();
+static_assert(f == a);
+
+
+constexpr int inc() {
+  int a = 10;
+  auto f = [&a]() {
+++a;
+  };
+
+  f();f();
+
+  return a;
+}
+static_assert(inc() == 12);
+
+constexpr int add(int a, int b) {
+  auto doIt = [a, b](int c) {
+return a + b + c;
+  };
+
+  return doIt(2);
+}
+static_assert(add(4, 5) == 11);
+
+
+constexpr int add2(int a, int b) {
+  auto doIt = [a, b](int c) {
+auto bar = [a]() { return a; };
+auto bar2 = [b]() { return b; };
+
+return bar() + bar2() + c;
+  };
+
+  return doIt(2);
+}
+static_assert(add2(4, 5) == 11);
+
+
+constexpr int div(int a, int b) {
+  auto f = [=]() {
+return a / b; // expected-note {{division by zero}} \
+  // ref-note {{division by zero}}
+  };
+
+  return f(); // expected-note {{in call to '&f->operator()()'}} \
+  // ref-note {{in call to '&f->operator()()'}}
+}
+static_assert(div(8, 2) == 4);
+static_assert(div(8, 0) == 4); // expected-error {{not an integral constant expression}} \
+   // expected-note {{in call to 'div(8, 0)'}} \
+   // ref-error {{not an integral constant expression}} \
+   // ref-note {{in call to 'div(8, 0)'}}
+
+
+struct F {
+  float f;
+};
+
+constexpr float captureStruct() {
+  F someF = {1.0};
+
+  auto p = [someF]() {
+return someF.f;
+  };
+
+  return p();
+}
+
+static_assert(captureStruct() == 1.0);
+
+namespace LambdaParams {
+  template
+  constexpr void callThis(T t) {
+return t();
+  }
+
+  constexpr int foo() {
+int a = 0;
+auto f = [&a]() { ++a; };
+
+callThis(f);
+
+return a;
+  }
+  /// FIXME: This should work in the new interpreter.
+  static_assert(foo() == 1); // expected-error {{not an integral constant expression}}
+}
+
Index: clang/lib/AST/Interp/Interp.h
===
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -850,6 +850,7 @@
   const Pointer &Field = Obj.atField(I);
   if (!CheckStore(S, OpPC, Field))
 return false;
+  Field.initialize();
   Field.deref() = Value;
   return true;
 }
Index: clang/lib/AST/Interp/EvalEmitter.h
===
--- clang/lib/AST/Interp/EvalEmitter.h
+++ clang/lib/AST/Interp/EvalEmitter.h
@@ -76,6 +76,10 @@
 
   /// Parameter indices.
   llvm::DenseMap Params;
+  /// Lambda captures.
+  /// Map from Decl* to [Offset, IsReference] pair.
+  llvm::DenseMap> LambdaCaptures;
+  unsigned LambdaThisCapture;
   /// Local descriptors.
   llvm::SmallVector, 2> Descriptors;
 
Index: clang/lib/AST/Interp/ByteCodeExprGen.h
===
--- clang/lib/AST/Interp/ByteCodeExprGen.h
+++ clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -93,6 +93,7 @@
   bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E);
   bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E);
   bool VisitTypeTraitExpr(const TypeTraitExpr *E);
+  bool VisitLambdaExpr(const LambdaExpr *E);
 
 protected:
   bool visitExpr(const Expr *E) override;
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -949,6 +949,43 @@
   return this->emitConstBool(E->getValue(), E);
 }
 
+template 
+bool ByteCodeExprGen::VisitLambdaExpr(const LambdaExpr *E) {
+  // XXX We assume here that a pointer-to-initialize is on the stack.
+
+  const Record *R = P.getOrCreateRecord(E->getLambdaClass());
+
+  auto *CaptureInitIt = E->capture_init_begin();
+  // Initialize all fields (which represent lambda captures) of the
+  // record with their initializers.
+  for (const Record::Field &F : R->fields()) {
+const Expr *Init = *CaptureInitIt;
+++CaptureInitIt;
+
+if (std::optional T = classify(Init)) {
+  if (!this->visit(Init))
+return false;
+
+  if (!this->emitSetField(*T, F.Offset, E))
+return false;
+} else {
+  if (!this->emitDupPtr(E))
+return false;
+
+  if (!t

[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-05-07 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder added inline comments.



Comment at: clang/test/AST/Interp/lambda.cpp:92
+  static_assert(foo() == 1); // expected-error {{not an integral constant 
expression}}
+}
+

aaron.ballman wrote:
> How about some tests like:
> ```
> constexpr int call_thru_func_ptr(int i) {
>   auto l = [](int i) { return i; };
>   int (*fp)(int) = l;
>   return fp(i);  
> }
> static_assert(call_thru_func_ptr(12) == 12);
> 
> constexpr int call_through_copied_lambda(auto lam, int i) {
>   auto copy = lam;
>   return copy(i);
> }
> 
> constexpr int call_through_copied_lambda(auto lam) {
>   auto copy = lam;
>   return copy();
> }
> 
> void func() {
>   constexpr int i = 12;
>   static_assert(call_through_copied_lambda([i]() { return i; }) == 12);
> }
> ```
Heh:
```
array.cpp:1245:15: error: static assertion expression is not an integral 
constant expression
 1245 | static_assert(call_thru_func_ptr(12) == 12);
  |   ^~~~
array.cpp:1243:10: note: non-constexpr function '__invoke' cannot be used in a 
constant expression
 1243 |   return fp(i);
  |  ^
array.cpp:1245:15: note: in call to 'call_thru_func_ptr(12)'
 1245 | static_assert(call_thru_func_ptr(12) == 12);
  |   ^
array.cpp:1239:12: note: declared here
 1239 |   auto l = [](int i) { return i; };
  |^

```


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-05-05 Thread Aaron Ballman via Phabricator via cfe-commits
aaron.ballman added inline comments.



Comment at: clang/lib/AST/Interp/ByteCodeEmitter.cpp:56-57
+  const Record *R = P.getOrCreateRecord(MD->getParent());
+  llvm::DenseMap _LambdaCaptures;
+  FieldDecl *_LambdaThisCapture;
+

UB alert -- `_L` is reserved; let's pick names without leading underscores or 
shadowing.



Comment at: clang/lib/AST/Interp/ByteCodeEmitter.h:73-74
   llvm::DenseMap Params;
+  /// Lambda captures.
+  llvm::DenseMap> LambdaCaptures;
+  unsigned *LambdaThisCapture;

Can you add comments explaining what the `unsigned, bool` pair is encoding?



Comment at: clang/lib/AST/Interp/ByteCodeEmitter.h:75
+  llvm::DenseMap> LambdaCaptures;
+  unsigned *LambdaThisCapture;
   /// Local descriptors.

Why is this a pointer? Since it's not really implemented, I'd recommend 
removing this for now and adding it back when you add support for capturing 
`this`.



Comment at: clang/test/AST/Interp/lambda.cpp:92
+  static_assert(foo() == 1); // expected-error {{not an integral constant 
expression}}
+}
+

How about some tests like:
```
constexpr int call_thru_func_ptr(int i) {
  auto l = [](int i) { return i; };
  int (*fp)(int) = l;
  return fp(i);  
}
static_assert(call_thru_func_ptr(12) == 12);

constexpr int call_through_copied_lambda(auto lam, int i) {
  auto copy = lam;
  return copy(i);
}

constexpr int call_through_copied_lambda(auto lam) {
  auto copy = lam;
  return copy();
}

void func() {
  constexpr int i = 12;
  static_assert(call_through_copied_lambda([i]() { return i; }) == 12);
}
```


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-05-01 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder added a comment.

Ping


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-04-21 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder added a comment.

Ping


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-04-12 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder added a comment.

Ping


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-04-04 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder added a comment.

Ping


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-03-27 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder added a comment.

Ping


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-03-23 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder added a comment.

Ping


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D146030/new/

https://reviews.llvm.org/D146030

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D146030: [clang][Interp] Handle LambdaExprs

2023-03-14 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder created this revision.
tbaeder added reviewers: aaron.ballman, erichkeane, tahonermann, shafik.
Herald added a project: All.
tbaeder requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Unfortunately I couldn't find an existing test file in `test/SemaCXX` that 
focused on lambdas in constant expressions.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D146030

Files:
  clang/lib/AST/Interp/ByteCodeEmitter.cpp
  clang/lib/AST/Interp/ByteCodeEmitter.h
  clang/lib/AST/Interp/ByteCodeExprGen.cpp
  clang/lib/AST/Interp/ByteCodeExprGen.h
  clang/lib/AST/Interp/EvalEmitter.h
  clang/lib/AST/Interp/Interp.h
  clang/test/AST/Interp/lambda.cpp

Index: clang/test/AST/Interp/lambda.cpp
===
--- /dev/null
+++ clang/test/AST/Interp/lambda.cpp
@@ -0,0 +1,93 @@
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify -std=c++20 %s
+// RUN: %clang_cc1 -verify=ref -std=c++20 %s
+
+constexpr int a = 12;
+constexpr int f = [c = a]() { return c; }();
+static_assert(f == a);
+
+
+constexpr int inc() {
+  int a = 10;
+  auto f = [&a]() {
+++a;
+  };
+
+  f();f();
+
+  return a;
+}
+static_assert(inc() == 12);
+
+constexpr int add(int a, int b) {
+  auto doIt = [a, b](int c) {
+return a + b + c;
+  };
+
+  return doIt(2);
+}
+static_assert(add(4, 5) == 11);
+
+
+constexpr int add2(int a, int b) {
+  auto doIt = [a, b](int c) {
+auto bar = [a]() { return a; };
+auto bar2 = [b]() { return b; };
+
+return bar() + bar2() + c;
+  };
+
+  return doIt(2);
+}
+static_assert(add2(4, 5) == 11);
+
+
+constexpr int div(int a, int b) {
+  auto f = [=]() {
+return a / b; // expected-note {{division by zero}} \
+  // ref-note {{division by zero}}
+  };
+
+  return f(); // expected-note {{in call to '&f->operator()()'}} \
+  // ref-note {{in call to '&f->operator()()'}}
+}
+static_assert(div(8, 2) == 4);
+static_assert(div(8, 0) == 4); // expected-error {{not an integral constant expression}} \
+   // expected-note {{in call to 'div(8, 0)'}} \
+   // ref-error {{not an integral constant expression}} \
+   // ref-note {{in call to 'div(8, 0)'}}
+
+
+struct F {
+  float f;
+};
+
+constexpr float captureStruct() {
+  F someF = {1.0};
+
+  auto p = [someF]() {
+return someF.f;
+  };
+
+  return p();
+}
+
+static_assert(captureStruct() == 1.0);
+
+namespace LambdaParams {
+  template
+  constexpr void callThis(T t) {
+return t();
+  }
+
+  constexpr int foo() {
+int a = 0;
+auto f = [&a]() { ++a; };
+
+callThis(f);
+
+return a;
+  }
+  /// FIXME: This should work in the new interpreter.
+  static_assert(foo() == 1); // expected-error {{not an integral constant expression}}
+}
+
Index: clang/lib/AST/Interp/Interp.h
===
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -760,6 +760,7 @@
   const Pointer &Field = Obj.atField(I);
   if (!CheckStore(S, OpPC, Field))
 return false;
+  Field.initialize();
   Field.deref() = Value;
   return true;
 }
Index: clang/lib/AST/Interp/EvalEmitter.h
===
--- clang/lib/AST/Interp/EvalEmitter.h
+++ clang/lib/AST/Interp/EvalEmitter.h
@@ -76,6 +76,9 @@
 
   /// Parameter indices.
   llvm::DenseMap Params;
+  /// Lambda captures.
+  llvm::DenseMap> LambdaCaptures;
+  unsigned LambdaThisCapture;
   /// Local descriptors.
   llvm::SmallVector, 2> Descriptors;
 
Index: clang/lib/AST/Interp/ByteCodeExprGen.h
===
--- clang/lib/AST/Interp/ByteCodeExprGen.h
+++ clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -92,6 +92,7 @@
   bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E);
   bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E);
   bool VisitTypeTraitExpr(const TypeTraitExpr *E);
+  bool VisitLambdaExpr(const LambdaExpr *E);
 
 protected:
   bool visitExpr(const Expr *E) override;
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -953,6 +953,43 @@
   return this->emitConstBool(E->getValue(), E);
 }
 
+template 
+bool ByteCodeExprGen::VisitLambdaExpr(const LambdaExpr *E) {
+  // XXX We assume here that a pointer-to-initialize is on the stack.
+
+  const Record *R = P.getOrCreateRecord(E->getLambdaClass());
+
+  auto *CaptureInitIt = E->capture_init_begin();
+  // Initialize all fields (which represent lambda captures) of the
+  // record with their initializers.
+  for (const Record::Field &F : R->fields()) {
+const Expr *Init = *CaptureInitIt;
+++CaptureInitIt;
+
+if (std::optional T = classify(Init)) {
+  if (!th