[PATCH] D36915: [Sema] Diagnose local variables and parameters captured by lambda and block expressions in a default argument

2018-01-17 Thread Richard Smith - zygoloid via Phabricator via cfe-commits
rsmith added a comment.

Please take a look at 
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0588r1.html, which 
respecifies the rules for lambda capture and its interaction with default 
arguments, and has been voted into the C++ working paper as a defect report 
resolution. The approach there is to use a purely syntactic, scope-based 
mechanism to detect problems such as this. (In dependent contexts, we can track 
on the `DeclRefExpr` whether the name is odr-usable, in case we can't tell 
whether it's odr-used from the template definition alone.)




Comment at: include/clang/Sema/Sema.h:1062
+
+  const DeclContext *ParentOfDefaultArg = nullptr;
+

There are lots of cases where we switch context in the middle of handling an 
expression, for instance to instantiate a template (or even *parse* a template 
in MSVC-compatible delayed template parsing mode). It's not reasonable to add a 
new form of state that all those places will need to save and restore 
themselves.

Please consider whether this would make sense as a member of the 
`ExpressionEvaluationContextRecord` or similar.



Comment at: lib/Sema/SemaExpr.cpp:4520-4541
+  // Add mappings for instantiated parameters appearing before Param. This
+  // is needed to instantiate default argument expressions referencing
+  // other parameters in unevaluated contexts.
+  if (FunctionDecl *Pattern = FD->getTemplateInstantiationPattern()) {
+auto I = FD->param_begin();
+for (const auto *PVD : Pattern->parameters()) {
+  if (*I == Param)

Use `addInstantiatedParametersToScope` for this.

This bugfix looks to be independent of the fix for lambdas; can you factor it 
out into a separate patch?


https://reviews.llvm.org/D36915



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


[PATCH] D36915: [Sema] Diagnose local variables and parameters captured by lambda and block expressions in a default argument

2017-12-12 Thread Aaron Ballman via Phabricator via cfe-commits
aaron.ballman added inline comments.



Comment at: lib/Sema/Sema.cpp:1677-1684
+Sema::DefaultArgRAII::DefaultArgRAII(Sema )
+: Actions(S), PrevParent(S.getParentOfDefaultArg()) {
+  S.setParentOfDefaultArg(S.CurContext);
+}
+
+Sema::DefaultArgRAII::~DefaultArgRAII() {
+  Actions.setParentOfDefaultArg(PrevParent);

Any reason not to inline this in the header file?



Comment at: lib/Sema/SemaExpr.cpp:4523
+  // other parameters in unevaluated contexts.
+  if (FunctionDecl *Pattern = FD->getTemplateInstantiationPattern()) {
+auto I = FD->param_begin();

`const FunctionDecl *`



Comment at: lib/Sema/SemaExpr.cpp:4535
+  unsigned NumExpansions =
+  *getNumArgumentsInExpansion(PVD->getType(), MutiLevelArgList);
+  CurrentInstantiationScope->MakeInstantiatedLocalArgPack(PVD);

Is it possible for the `Optional<>` returned here to not hold a value?



Comment at: lib/Sema/SemaExpr.cpp:13746-13751
+unsigned DiagID;
+if (isa(var))
+  DiagID = diag::err_param_default_argument_references_param;
+else
+  DiagID = diag::err_param_default_argument_references_local;
+S.Diag(loc, DiagID) << var->getDeclName();

Ternary operator in `S.Diag()` would be just as clear, I think.

Also, you should not need to call `getDeclName()` as the diagnostic engine will 
automatically do the right thing. As is stands, this won't properly quote the 
name in the diagnostic.


https://reviews.llvm.org/D36915



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


[PATCH] D36915: [Sema] Diagnose local variables and parameters captured by lambda and block expressions in a default argument

2017-12-12 Thread John McCall via Phabricator via cfe-commits
rjmccall added a comment.

This looks good to me.


https://reviews.llvm.org/D36915



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


[PATCH] D36915: [Sema] Diagnose local variables and parameters captured by lambda and block expressions in a default argument

2017-12-08 Thread Akira Hatanaka via Phabricator via cfe-commits
ahatanak added a comment.

ping


https://reviews.llvm.org/D36915



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


[PATCH] D36915: [Sema] Diagnose local variables and parameters captured by lambda and block expressions in a default argument

2017-09-29 Thread Akira Hatanaka via Phabricator via cfe-commits
ahatanak added a comment.

ping.

Any comments on this patch or alternate approaches?


https://reviews.llvm.org/D36915



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


[PATCH] D36915: [Sema] Diagnose local variables and parameters captured by lambda and block expressions in a default argument

2017-09-19 Thread Akira Hatanaka via Phabricator via cfe-commits
ahatanak added a comment.

ping


https://reviews.llvm.org/D36915



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


[PATCH] D36915: [Sema] Diagnose local variables and parameters captured by lambda and block expressions in a default argument

2017-09-11 Thread Akira Hatanaka via Phabricator via cfe-commits
ahatanak updated this revision to Diff 114753.
ahatanak added a comment.

Address review comments.

- Detect invalid references to parameters or local variables by default 
arguments in tryCaptureVariable. Before parsing or instantiating the default 
argument expression, the enclosing DeclContext is saved to ParentOfDefaultArg, 
which tryCaptureVariable uses to detect invalid references (if the referenced 
variable belongs to ParentOfDefaultArg or an enclosing DeclContext, it is not 
valid).

- In CheckCXXDefaultArgExpr, save the parameters and their instantiations that 
appear before the parameter with default argument to the current 
LocalInstantiationScope so that findInstantiationOf doesn't assert when it 
tries to find the instantiation of a parameter that is referenced in the 
default arugment.

There are still cases where clang rejects references to local variables or 
parameters that shouldn't be rejected. For example:

- Local variables or parameters referenced in _Generic's controlling-expression 
or the expressions of the selections that are not chosen.
- The following code is rejected even though 'x' is not odr-used:

  void func() {
const int x = 1;
void foo1(int a0 = x);
  }



- dcl.fct.default/p7.cpp

I plan to work on a fix after this patch is committed.


https://reviews.llvm.org/D36915

Files:
  include/clang/Sema/Sema.h
  lib/Parse/ParseDecl.cpp
  lib/Sema/Sema.cpp
  lib/Sema/SemaExpr.cpp
  test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp
  test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp
  test/SemaCXX/default1.cpp
  test/SemaObjCXX/blocks.mm

Index: test/SemaObjCXX/blocks.mm
===
--- test/SemaObjCXX/blocks.mm
+++ test/SemaObjCXX/blocks.mm
@@ -169,3 +169,17 @@
   return b; // expected-error {{no viable conversion from returned value of type 'MoveBlockVariable::B0' to function return type 'MoveBlockVariable::B1'}}
 }
 }
+
+namespace DefaultArg {
+void test() {
+  id x;
+  void func0(id a0, id a1 = ^{ (void) }); // expected-error {{default argument references parameter 'a0'}}
+  void func1(id a0, id a1 = ^{ (void) }); // expected-error {{default argument references local variable 'x' of enclosing function}}
+  void func2(id a0, id a1 = ^{ (void)sizeof(a0); });
+  void func3(id a0 = ^{ (void)sizeof(x); });
+  void func4(id a0, id a1 = ^{
+^{ (void) }(); // expected-error {{default argument references parameter 'a0'}}
+[=](){ (void) }(); // expected-error {{default argument references parameter 'a0'}}
+  });
+}
+}
Index: test/SemaCXX/default1.cpp
===
--- test/SemaCXX/default1.cpp
+++ test/SemaCXX/default1.cpp
@@ -78,3 +78,79 @@
 
 void PR20769_b(int = 1);
 void PR20769_b() { void PR20769_b(int = 2); }
+
+#if __cplusplus >= 201103L // C++11 or later
+struct S2 {
+  template
+  S2(T&&) {}
+};
+
+template
+void func0(int a0, S2 a1 = [](){ (void) }); // expected-error {{default argument references parameter 'a0'}}
+
+// FIXME: There shouldn't be any warnings about variables referenced in the
+//controlling-expression or the expressions of the selections that are
+//not chosen.
+template
+void func1(T a0, int a1, S2 a2 = _Generic((a0), default: [](){ (void) }, int: 0)); // expected-error {{default argument references parameter 'a0'}}
+
+template
+void func2(S2 a0 = [](){
+  int t; [](){ (void)}();
+});
+
+template
+void func3(int a0, S2 a1 = [](){
+  [=](){ (void)}(); // expected-error {{default argument references parameter 'a0'}}
+});
+
+void func4(int, int);
+
+template
+void func5(Ts...a0, S2 a1 = [](){ func4(a0...); }) { // expected-error 2 {{default argument references parameter 'a0'}}
+}
+
+template
+void func6(Ts...a0, S2 a1 = [](){ (void)sizeof...(a0); }) {
+}
+
+double d;
+
+void test1() {
+  int i;
+  float f;
+  // FIXME: There shouldn't be any warnings about variables referenced in the
+  //controlling-expression or the expressions of the selections that are
+  //not chosen.
+  void foo0(int a0 = _Generic((f), double: d, float: f)); // expected-error 2 {{default argument references local variable 'f' of enclosing function}}
+  void foo1(int a0 = _Generic((d), double: d, float: f)); // expected-error {{default argument references local variable 'f' of enclosing function}}
+  void foo2(int a0 = _Generic((i), int: d, float: f)); // expected-error {{default argument references local variable 'i' of enclosing function}} expected-error {{default argument references local variable 'f' of enclosing function}}
+  void foo3(int a0 = _Generic((i), default: d, float: f)); // expected-error {{default argument references local variable 'i' of enclosing function}} expected-error {{default argument references local variable 'f' of enclosing function}}
+
+  void foo4(S2 a0 = [&](){ (void) }); // expected-error {{default argument references local variable 'i' of enclosing function}}
+  void foo5(S2 a0 = [](){
+// No 

Re: [PATCH] D36915: [Sema] Diagnose local variables and parameters captured by lambda and block expressions in a default argument

2017-08-28 Thread Richard Smith via cfe-commits
On 28 August 2017 at 13:47, Akira Hatanaka via Phabricator via cfe-commits <
cfe-commits@lists.llvm.org> wrote:

> clang currently rejects "void foo(int = a);"  and so does gcc.
>
> I'm trying to search the defect reports, but it looks like the c++
> standard's site is currently unreachable.


It's http://lists.isocpp.org/core/2017/04/2108.php, but that's too recent
for the latest issues list (
http://wiki.edg.com/pub/Wg21albuquerque/CoreWorkingGroup/cwg_active.html).
(Apologies for the committee-internal links; as you say, the public site is
down.)
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36915: [Sema] Diagnose local variables and parameters captured by lambda and block expressions in a default argument

2017-08-28 Thread Akira Hatanaka via Phabricator via cfe-commits
ahatanak added a comment.

clang currently rejects "void foo(int = a);"  and so does gcc.

I'm trying to search the defect reports, but it looks like the c++ standard's 
site is currently unreachable.


https://reviews.llvm.org/D36915



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


[PATCH] D36915: [Sema] Diagnose local variables and parameters captured by lambda and block expressions in a default argument

2017-08-28 Thread Faisal Vali via Phabricator via cfe-commits
faisalv added a comment.

In https://reviews.llvm.org/D36915#854317, @ahatanak wrote:

> In https://reviews.llvm.org/D36915#849622, @faisalv wrote:
>
> > I don't think this approach is entirely correct for at least the following 
> > reasons:
> >
> > 1. in the lambda case the machinery that diagnoses capture failures should 
> > be the machinery erroring on the lambda (when the parameter is odr-used)
>
>
> Does this mean that it is OK to have a parameter or local variable appear in 
> a potentially-evaluated expression as long as it is not odr-used? I think 
> this is slightly different from the standard, which says a parameter or local 
> variable cannot appear as a potentially-evaluated expression in a default 
> argument.
>
> For example:
>
>   void foo2(int);
>  
>   void func() {
> const int a = 1;
> void foo1(S s = [](){ foo2(a); }); // "a" is not in an unevaluated 
> context and is not odr-used.
>   }
>  
>
>
> I think I need clarification as it will affect how or where we detect this 
> error.


'a' is not captured (and does not need to be captured) above, so I think that 
code should be ok.

But I also think the following code should be ok at local scope within func.
void foo(int = a);

I thought there was a DR against the standard-draft with the intent of making 
these valid (if they are not already so)?


https://reviews.llvm.org/D36915



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


[PATCH] D36915: [Sema] Diagnose local variables and parameters captured by lambda and block expressions in a default argument

2017-08-28 Thread Akira Hatanaka via Phabricator via cfe-commits
ahatanak added a comment.

In https://reviews.llvm.org/D36915#849622, @faisalv wrote:

> I don't think this approach is entirely correct for at least the following 
> reasons:
>
> 1. in the lambda case the machinery that diagnoses capture failures should be 
> the machinery erroring on the lambda (when the parameter is odr-used)


Does this mean that it is OK to have a parameter or local variable appear in a 
potentially-evaluated expression as long as it is not odr-used? I think this is 
slightly different from the standard, which says a parameter or local variable 
cannot appear as a potentially-evaluated expression in a default argument.

For example:

  void foo2(int);
  
  void func() {
const int a = 1;
void foo1(S s = [](){ foo2(a); }); // "a" is not in an unevaluated context 
and is not odr-used.
  }

I think I need clarification as it will affect how or where we detect this 
error.


https://reviews.llvm.org/D36915



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


[PATCH] D36915: [Sema] Diagnose local variables and parameters captured by lambda and block expressions in a default argument

2017-08-24 Thread Akira Hatanaka via Phabricator via cfe-commits
ahatanak added a comment.

In https://reviews.llvm.org/D36915#849622, @faisalv wrote:

> I don't think this approach is entirely correct for at least the following 
> reasons:
>
> 1. in the lambda case the machinery that diagnoses capture failures should be 
> the machinery erroring on the lambda (when the parameter is odr-used)
> 2. in the unevaluated case, once you disable the error, the instantiation 
> machinery will fail to find the corresponding instantiated parmvardecl.


Oh right, it stills assert in the unevaluated case. I should have a test case 
for that too.

I also found that the following code, which has a lambda that doesn't capture 
anything, asserts (in IRGen):

  struct S {
template
S(T&&) {}
  };
  
  template
  void foo1() {
struct S2 {
  void foo2(S s = [](){}) {
  }
};
  
S2 s2;
s2.foo2();
  }
  
  void foo3() {
foo1();
  }


https://reviews.llvm.org/D36915



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


[PATCH] D36915: [Sema] Diagnose local variables and parameters captured by lambda and block expressions in a default argument

2017-08-22 Thread Faisal Vali via Phabricator via cfe-commits
faisalv added a comment.

I don't think this approach is entirely correct for at least the following 
reasons:

1. in the lambda case the machinery that diagnoses capture failures should be 
the machinery erroring on the lambda (when the parameter is odr-used)
2. in the unevaluated case, once you disable the error, the instantiation 
machinery will fail to find the corresponding instantiated parmvardecl.

I think - in addition to allowing unevaluated uses of parmvardecls by tweaking 
the DefaultArgumentChecker, you would also need to add the instantiated 
mappings of the parameters, prior to instantiating the default argument (to 
avoid the assertion) and perhaps need to tweak DoMarkVarDeclReferenced and/or 
tryCaptureVariable to make sure such cases for lambdas produce errors (if they 
don't, w the above fix).

Thanks for working on this!


https://reviews.llvm.org/D36915



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


[PATCH] D36915: [Sema] Diagnose local variables and parameters captured by lambda and block expressions in a default argument

2017-08-18 Thread Akira Hatanaka via Phabricator via cfe-commits
ahatanak created this revision.

This patch fixes an assertion failure that occurs when compiling the following 
invalid code:

  struct S {
template
S(T &&) {}
  };
  
  template
  void foo1(int a0, S a1 = [](){ (void) } ) { // a0 cannot be used in the 
default argument for a1
  }
  
  void foo2() {
foo1(1);
  }

$ clang++ -std=c++14 -c -o /dev/null test.cpp

Assertion failed: (isa(D) && "declaration not instantiated in this 
scope"), function findInstantiationOf, file 
llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp, line 2911.

The assertion fails when findInstantiationOf is called to find the instantiated 
decl of a0 when instantiating the lambda expression that is the default 
argument for a1.

To prevent the assertion failure, this patch makes CheckDefaultArgumentVisitor 
visit all subexpressions belonging to a default argument expression and detect 
local variables and parameters (that are external to the default argument) 
referenced in the default argument expression after the template definition is 
parsed. This patch also removes the diagnostic that is printed in test p7.cpp 
when a local variable is referenced inside a unevaluated default argument 
expression, which I think conforms to c++14 or later.

Also, with this patch, clang prints diagnostics when local variables or 
parameters are referenced inside a block expression that is used as a default 
argument. I wasn't 100% sure it is legal to use blocks for default arguments (I 
found that compiling the code below causes clang to segfault), but it seems to 
me that we want to handle blocks in default arguments the same way we handle 
lambdas.

  void logRange(id i = [](){}) {
  }
  
  void foo1() {
logRange();
  }

$ clang++ -std=c++14 -c -o /dev/null -fobjc-arc test.mm

rdar://problem/33239958


https://reviews.llvm.org/D36915

Files:
  lib/Sema/SemaDeclCXX.cpp
  test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp
  test/SemaCXX/default1.cpp
  test/SemaObjCXX/blocks.mm

Index: test/SemaObjCXX/blocks.mm
===
--- test/SemaObjCXX/blocks.mm
+++ test/SemaObjCXX/blocks.mm
@@ -169,3 +169,17 @@
   return b; // expected-error {{no viable conversion from returned value of type 'MoveBlockVariable::B0' to function return type 'MoveBlockVariable::B1'}}
 }
 }
+
+namespace DefaultArg {
+void test() {
+  id x;
+  void func0(id a0, id a1 = ^{ (void) }); // expected-error {{default argument references parameter 'a0'}}
+  void func1(id a0, id a1 = ^{ (void) }); // expected-error {{default argument references local variable 'x' of enclosing function}}
+  void func2(id a0, id a1 = ^{ (void)sizeof(a0); });
+  void func3(id a0 = ^{ (void)sizeof(x); });
+  void func4(id a0, id a1 = ^{
+^{ (void) }(); // expected-error {{default argument references parameter 'a0'}}
+[=](){ (void) }(); // expected-error {{default argument references parameter 'a0'}}
+  });
+}
+}
Index: test/SemaCXX/default1.cpp
===
--- test/SemaCXX/default1.cpp
+++ test/SemaCXX/default1.cpp
@@ -78,3 +78,60 @@
 
 void PR20769_b(int = 1);
 void PR20769_b() { void PR20769_b(int = 2); }
+
+#if __cplusplus >= 201103L // C++11 or later
+struct S2 {
+  template
+  S2(T&&) {}
+};
+
+template
+void func0(int a0, S2 a1 = [](){ (void) }); // expected-error {{default argument references parameter 'a0'}}
+
+template
+void func1(T a0, int a1, S2 a2 = _Generic((a0), default: [](){ (void) }, int: 0)); // expected-error {{default argument references parameter 'a1'}}
+
+template
+void func2(S2 a0 = [](){
+  int t; [](){ (void)}();
+});
+
+template
+void func3(int a0, S2 a1 = [](){
+  [=](){ (void)}(); // expected-error {{default argument references parameter 'a0'}}
+});
+
+double d;
+
+void test1() {
+  int i;
+  float f;
+  void foo0(int a0 = _Generic((f), double: d, float: f)); // expected-error {{default argument references local variable 'f' of enclosing function}}
+  void foo1(int a0 = _Generic((d), double: d, float: f));
+  void foo2(int a0 = _Generic((i), int: d, float: f));
+  void foo3(int a0 = _Generic((i), default: d, float: f));
+
+  void foo4(S2 a0 = [&](){ (void) }); // expected-error {{lambda expression in default argument cannot capture any entity}}
+  void foo5(S2 a0 = [](){
+// No warning about capture list of a lambda expression defined in a block scope.
+int t; [](){ (void)}();
+  });
+  void foo6(int a0, S2 a1 = [](){
+// No warning about local variables or parameters referenced by an
+// unevaluated expression.
+int t = sizeof({i, a0;});
+  });
+  void foo6(S2 a0 = [](){
+int i; // expected-note {{'i' declared here}}
+void foo7(int a0, // expected-note {{'a0' declared here}}
+  S2 a1 = [](){ (void) }); // expected-error {{variable 'a0' cannot be implicitly captured in a lambda with no capture-default specified}} expected-error {{default argument references parameter 'a0'}} expected-note {{lambda