From: Eli Friedman Date: Tue, 7 Feb 2012 00:15:00 +0000 (+0000) Subject: Misc improvements to the diagnostic when a variable is odr-used in a context that... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0a29422eb722c0ffbb98b98d8636042b19069f1a;p=clang Misc improvements to the diagnostic when a variable is odr-used in a context that is not allowed to capture variables. Fixes PR11883. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149937 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 7eef491e48..e52b35e1cd 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -4636,7 +4636,14 @@ def ext_ms_anonymous_struct : ExtWarn< // C++ local classes def err_reference_to_local_var_in_enclosing_function : Error< - "reference to local variable %0 declared in enclosed function %1">; + "reference to local variable %0 declared in enclosing function %1">; +def err_reference_to_local_var_in_enclosing_block : Error< + "reference to local variable %0 declared in enclosing block literal">; +def err_reference_to_local_var_in_enclosing_lambda : Error< + "reference to local variable %0 declared in enclosing lambda expression">; +def err_reference_to_local_var_in_enclosing_context : Error< + "reference to local variable %0 declared in enclosing context">; + def note_local_variable_declared_here : Note< "%0 declared here">; def err_static_data_member_not_allowed_in_local_class : Error< diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 8afe0c25b9..c21979758d 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -9471,29 +9471,47 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) { static void diagnoseUncapturableValueReference(Sema &S, SourceLocation loc, VarDecl *var, DeclContext *DC) { + DeclContext *VarDC = var->getDeclContext(); + // If the parameter still belongs to the translation unit, then // we're actually just using one parameter in the declaration of // the next. if (isa(var) && - isa(var->getDeclContext())) + isa(VarDC)) return; - // Don't diagnose about capture if we're not actually in code right - // now; in general, there are more appropriate places that will - // diagnose this. - // FIXME: We incorrectly skip diagnosing C++11 member initializers. - if (!S.CurContext->isFunctionOrMethod()) + // For C code, don't diagnose about capture if we're not actually in code + // right now; it's impossible to write a non-constant expression outside of + // function context, so we'll get other (more useful) diagnostics later. + // + // For C++, things get a bit more nasty... it would be nice to suppress this + // diagnostic for certain cases like using a local variable in an array bound + // for a member of a local class, but the correct predicate is not obvious. + if (!S.getLangOptions().CPlusPlus && !S.CurContext->isFunctionOrMethod()) return; - DeclarationName functionName; - if (FunctionDecl *fn = dyn_cast(var->getDeclContext())) - functionName = fn->getDeclName(); - // FIXME: variable from enclosing block that we couldn't capture from! + if (isa(VarDC) && + cast(VarDC->getParent())->isLambda()) { + S.Diag(loc, diag::err_reference_to_local_var_in_enclosing_lambda) + << var->getIdentifier(); + } else if (FunctionDecl *fn = dyn_cast(VarDC)) { + S.Diag(loc, diag::err_reference_to_local_var_in_enclosing_function) + << var->getIdentifier() << fn->getDeclName(); + } else if (isa(VarDC)) { + S.Diag(loc, diag::err_reference_to_local_var_in_enclosing_block) + << var->getIdentifier(); + } else { + // FIXME: Is there any other context where a local variable can be + // declared? + S.Diag(loc, diag::err_reference_to_local_var_in_enclosing_context) + << var->getIdentifier(); + } - S.Diag(loc, diag::err_reference_to_local_var_in_enclosing_function) - << var->getIdentifier() << functionName; S.Diag(var->getLocation(), diag::note_local_variable_declared_here) << var->getIdentifier(); + + // FIXME: Add additional diagnostic info about class etc. which prevents + // capture. } static bool shouldAddConstForScope(CapturingScopeInfo *CSI, VarDecl *VD) { diff --git a/test/CXX/class/class.local/p1-0x.cpp b/test/CXX/class/class.local/p1-0x.cpp new file mode 100644 index 0000000000..8916295825 --- /dev/null +++ b/test/CXX/class/class.local/p1-0x.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s + +void f() { + int x = 3; // expected-note{{'x' declared here}} + const int c = 2; + struct C { + int& x2 = x; // expected-error{{reference to local variable 'x' declared in enclosing function 'f'}} + int cc = c; + }; + []() mutable { // expected-error {{not supported yet}} + int x = 3; // expected-note{{'x' declared here}} + struct C { + int& x2 = x; // expected-error{{reference to local variable 'x' declared in enclosing lambda expression}} + }; + } + C(); +} + diff --git a/test/CXX/class/class.local/p1.cpp b/test/CXX/class/class.local/p1.cpp index 05ae5c71d1..62ade5cb88 100644 --- a/test/CXX/class/class.local/p1.cpp +++ b/test/CXX/class/class.local/p1.cpp @@ -8,7 +8,7 @@ void f() extern int g(); struct local { - int g() { return x; } // expected-error{{reference to local variable 'x' declared in enclosed function 'f'}} + int g() { return x; } // expected-error{{reference to local variable 'x' declared in enclosing function 'f'}} int h() { return s; } int k() { return :: x; } int l() { return g(); } diff --git a/test/CXX/class/class.local/p3.cpp b/test/CXX/class/class.local/p3.cpp index c24d5d8a09..3753790384 100644 --- a/test/CXX/class/class.local/p3.cpp +++ b/test/CXX/class/class.local/p3.cpp @@ -24,7 +24,7 @@ void f2() { void f3(int a) { // expected-note{{'a' declared here}} struct X { struct Y { - int f() { return a; } // expected-error{{reference to local variable 'a' declared in enclosed function 'f3'}} + int f() { return a; } // expected-error{{reference to local variable 'a' declared in enclosing function 'f3'}} }; }; } diff --git a/test/SemaCXX/c99-variable-length-array.cpp b/test/SemaCXX/c99-variable-length-array.cpp index 2f9bb957ec..7773c0849b 100644 --- a/test/SemaCXX/c99-variable-length-array.cpp +++ b/test/SemaCXX/c99-variable-length-array.cpp @@ -73,10 +73,11 @@ void test_accept_array(int N) { } // Variably-modified types cannot be used in local classes. -void local_classes(int N) { +void local_classes(int N) { // expected-note {{declared here}} struct X { int size; int array[N]; // expected-error{{fields must have a constant size: 'variable length array in structure' extension will never be supported}} \ + // expected-error{{reference to local variable 'N' declared in enclosing function 'local_classes'}} \ // expected-warning{{variable length arrays are a C99 feature}} }; }