PR14296: function parameter name collisions in function try/catch

C++11 3.3.3/2 "A parameter name shall not be redeclared in the outermost block
of the function definition nor in the outermost block of any handler associated
with a function-try-block."

It's not totally clear to me whether the "FIXME" case is covered by this, but
Richard Smith thinks it probably should be. It's just a bit more involved to
fix that case.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@167650 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index 0968d9c..c433344 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -1563,8 +1563,8 @@
   // C++ 6: Statements and Blocks
 
   StmtResult ParseCXXTryBlock();
-  StmtResult ParseCXXTryBlockCommon(SourceLocation TryLoc);
-  StmtResult ParseCXXCatchBlock();
+  StmtResult ParseCXXTryBlockCommon(SourceLocation TryLoc, bool FnTry = false);
+  StmtResult ParseCXXCatchBlock(bool FnCatch = false);
 
   //===--------------------------------------------------------------------===//
   // MS: SEH Statements and Blocks
diff --git a/include/clang/Sema/Scope.h b/include/clang/Sema/Scope.h
index b78556e..fa508cf 100644
--- a/include/clang/Sema/Scope.h
+++ b/include/clang/Sema/Scope.h
@@ -82,7 +82,13 @@
     SwitchScope = 0x800,
 
     /// TryScope - This is the scope of a C++ try statement.
-    TryScope = 0x1000
+    TryScope = 0x1000,
+
+    /// FnTryScope - This is the scope of a function-level C++ try scope.
+    FnTryScope = 0x3000,
+
+    /// FnCatchScope - This is the scope of a function-level C++ catch scope.
+    FnCatchScope = 0x4000
   };
 private:
   /// The parent scope for this scope.  This is null for the translation-unit
diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp
index 4b5bdc2..f604e03 100644
--- a/lib/Parse/ParseStmt.cpp
+++ b/lib/Parse/ParseStmt.cpp
@@ -2051,7 +2051,7 @@
   }
 
   SourceLocation LBraceLoc = Tok.getLocation();
-  StmtResult FnBody(ParseCXXTryBlockCommon(TryLoc));
+  StmtResult FnBody(ParseCXXTryBlockCommon(TryLoc, /*FnTry*/true));
   // If we failed to parse the try-catch, we just give the function an empty
   // compound statement as the body.
   if (FnBody.isInvalid()) {
@@ -2117,13 +2117,14 @@
 ///         'try' compound-statement seh-except-block
 ///         'try' compound-statment  seh-finally-block
 ///
-StmtResult Parser::ParseCXXTryBlockCommon(SourceLocation TryLoc) {
+StmtResult Parser::ParseCXXTryBlockCommon(SourceLocation TryLoc, bool FnTry) {
   if (Tok.isNot(tok::l_brace))
     return StmtError(Diag(Tok, diag::err_expected_lbrace));
   // FIXME: Possible draft standard bug: attribute-specifier should be allowed?
 
   StmtResult TryBlock(ParseCompoundStatement(/*isStmtExpr=*/false,
-                                             Scope::DeclScope|Scope::TryScope));
+                      Scope::DeclScope |
+                        (FnTry ? Scope::FnTryScope : Scope::TryScope)));
   if (TryBlock.isInvalid())
     return TryBlock;
 
@@ -2159,7 +2160,7 @@
     if (Tok.isNot(tok::kw_catch))
       return StmtError(Diag(Tok, diag::err_expected_catch));
     while (Tok.is(tok::kw_catch)) {
-      StmtResult Handler(ParseCXXCatchBlock());
+      StmtResult Handler(ParseCXXCatchBlock(FnTry));
       if (!Handler.isInvalid())
         Handlers.push_back(Handler.release());
     }
@@ -2183,7 +2184,7 @@
 ///         type-specifier-seq
 ///         '...'
 ///
-StmtResult Parser::ParseCXXCatchBlock() {
+StmtResult Parser::ParseCXXCatchBlock(bool FnCatch) {
   assert(Tok.is(tok::kw_catch) && "Expected 'catch'");
 
   SourceLocation CatchLoc = ConsumeToken();
@@ -2195,7 +2196,8 @@
   // C++ 3.3.2p3:
   // The name in a catch exception-declaration is local to the handler and
   // shall not be redeclared in the outermost block of the handler.
-  ParseScope CatchScope(this, Scope::DeclScope | Scope::ControlScope);
+  ParseScope CatchScope(this, Scope::DeclScope | Scope::ControlScope |
+                          (FnCatch ? Scope::FnCatchScope : 0));
 
   // exception-declaration is equivalent to '...' or a parameter-declaration
   // without default arguments.
diff --git a/lib/Sema/IdentifierResolver.cpp b/lib/Sema/IdentifierResolver.cpp
index 4d62cab..50413c3 100644
--- a/lib/Sema/IdentifierResolver.cpp
+++ b/lib/Sema/IdentifierResolver.cpp
@@ -135,8 +135,13 @@
       // of the controlled statement.
       //
       assert(S->getParent() && "No TUScope?");
-      if (S->getParent()->getFlags() & Scope::ControlScope)
+      if (S->getFlags() & Scope::FnTryScope)
         return S->getParent()->isDeclScope(D);
+      if (S->getParent()->getFlags() & Scope::ControlScope) {
+        if (S->getParent()->getFlags() & Scope::FnCatchScope)
+          S = S->getParent();
+        return S->getParent()->isDeclScope(D);
+      }
     }
     return false;
   }
diff --git a/test/CXX/basic/basic.scope/basic.scope.local/p2.cpp b/test/CXX/basic/basic.scope/basic.scope.local/p2.cpp
new file mode 100644
index 0000000..624118c
--- /dev/null
+++ b/test/CXX/basic/basic.scope/basic.scope.local/p2.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -fexceptions -verify %s
+
+void func1(int i) { // expected-note{{previous definition is here}}
+  int i; // expected-error{{redefinition of 'i'}}
+}
+
+void func2(int i) try { // expected-note{{previous definition is here}}
+  int i; // expected-error{{redefinition of 'i'}}
+} catch (...) {
+}
+
+void func3(int i) try { // FIXME: note {{previous definition is here}}
+} catch (int i) { // FIXME: error {{redefinition of 'i'}}
+}
+
+void func4(int i) try { // expected-note{{previous definition is here}}
+} catch (...) {
+  int i; // expected-error{{redefinition of 'i'}}
+}
+
+void func5() try {
+  int i;
+} catch (...) {
+  int j = i; // expected-error{{use of undeclared identifier 'i'}}
+}